LOCAL EXAMINATION described below: Examination of the thyroid swelling should be made as discussed in Chapter 3 under 'Examination of a swelling, besides these examinations peculiar to the thyroid gland will be described below A INSPECTION Normal thyroid gland is not obvious on inspection. It can be seen only when the thyroid gland is swollen. In case of obese and short-necked individual inspection of the thyroid gland becomes more difficult. To render inspection easier one can follow Pizzillo's method (Figs. 27.1A and B) in which the hands are placed behind the head and the patient is asked to push her head backwards against her clasped hands on the occiput. The thyroid swelling may be uniform involving the whole of the thyroid gland (physiological goiter, colloid goiter (Fig. 27.2), Hashimoto's disease. etc.) or isolated nodules of different sizes may be seen in the thyroid region (Figs. 27.3 and 27.4) (nodular goiter). Rarely, a swelling on the lateral side of the neck is not due to enlargement of an aberrant thyroid gland but is caused by metastasis in lymph nodes from hidden carcinoma of the thyroid gland. Ask the patient to swallow and watch B for the most important physical sign-a thyroid swelling moves upwards on deglutition. This is due to the fact that the thyroid gland is fixed to the larynx. Other A swellings which may move on deglutition Figs. 27.3A and B: Nodular goiter. Note how the swelling moves up during swallowing in the second picture. are thyroglossal cysts, subhyoid bursitis and prelaryngeal or pretracheal lymph nodes fixed to the larynx or trachea. Such movement of the thyroid becomes greatly limited when it is fixed by inflammation or malignant infiltration. In retrosternal goiter, pressure on the great veins at the thoracic inlet gives rise to dilatation of the subcutaneous veins over the upper anterior part of the thorax. When these are present, ask the patient to swallow and determine, on inspection, the lower border of the swelling as it moves up on deglutition. This is not possible in case of retrosternal goiter. The patient should be asked to raise both the arms over his head until they touch the ears. This position is maintained for a while. Congestion of face and distress become evident in case of retrosternal goiter due to obstruction of the great veins at the thoracic inlet. A thyroglossal cyst (Fig. 27.5) also moves upwards on deglutition. But the pathognomonic feature is that it moves upwards with protrusion of the tongue since the thyroglossal duct extends downwards from the foramen caecum of the tongue to the isthmus of thyroid gland (Figs. 27.6A and B). Thyroglossal fistula is seen near the midline a little below the hyoid bone. The opening of the fistula is indrawn and overlaid by a crescentic fold of skin. PALPATION: The thyroid gland should always be palpated with the patient's neck slightly flexed. The gland may be palpated from behind and from the front. The patient A should be sitted on a stool and the clinician stands behind the patient. The patient is asked to flex the neck slightly. The thumbs of both the hands are placed behind the neck and the other four fingers of each hand are placed on each lobe and the isthmus (Figs. 27.7 and 27.8). Palpation should be carried out in their entirety. Careful assessment of the lower margin. Additional information about one lobe may be obtained by relaxing the sternomastoid muscle of that side by flexing and rotating the face to the same side. To get more information about a particular nodule of the thyroid gland one may ask the patient to extend the neck. This only makes the nodule more prominent for better the margins of the thyroid gland is important, particularly palpation. Figs. 27.6A and B: Show that the thyroglos sal cyst moves up with protrusion of the tongue. Palpation of each lobe is best carried out by Lahey's method (Figs. 27.9 and 27.10). In this case the examiner stands in front of the patient. To palpate the left lobe properly, the thyroid gland is pushed to the left from the right side by the left hand of the examiner. This makes the left lobe more prominen. so that the examiner can palpate it thoroughly with his right hand. During palpation the patient should be asked to swallow in order to settle the diagnosis of the thyroid swelling. Slight enlargement of the thyroid gland or presence of nodules in its substance can be appreciated by simply placing the thumb on the thyroid gland while the patient swallows. (Crile's method). During palpation the following points should be noted: (1) Whether the whole thyroid gland is enlarged? If so, note its surface-whether it is smooth (primary thyrotoxicosis or colloid goiter) or bosselated (multinodular goiter) and its consistency whether uniform or variable. It may be firm in case of primary thyrotoxicosis, Hashimoto's disease, etc., it is slightly softer in colloid goiter and hard in Riedel's thyroiditis or carcinoma in which the consistency may be variable in places. Fig. 27.7: Shows how to get below the thyroid swelling patient is asked to swallow. The thyroid swelling moves to exclude presence of retrosternal prolongation. The up. Clinician now puts his fingers at the lower margin of the thyroid to be sure that there is no further downward extension of the thyroid tissue. (ii) When a swelling is localized, note its position, size, shape, extent and its consistency. It must be remembered that a cystic swelling in the thyroid gland often feels firm due to great tension within the cyst which is surrounded by relatively soft surrounding tissue of the gland. A calcified cyst may even feel hard. (iii) The mobility should be noted in both horizontal and vertical planes. Fixity means malignant tumor or chronic thyroiditis. (iv) To get below the thyroid gland is an important test to discard the possibility of retrosternal extension. Clinician's index finger is placed on the lower border of the thyroid gland. The patient is asked to swallow, the thyroid gland will move up and the lower border is palpated carefully for any extension downwards (Fig. 27.11). ) Pressure effect from the thyroid swelling should be carefully looked for. Pressure may be on the trachea (Fig. 27.12) or larynx, which may lead to stridor (inspiratory noise of inrushing narrowed trachea) and later on dyspnea. Pressure may be on the air through esophagus which may lead to dysphagia. Pressure may be on the recurrent laryngeal nerve, which may lead to hoarseness of voice. Pressure may be on even the carotid sheath (Fig. 27.13). If pressure trachea is suspected, slight push on the lateral lobes will produce stridor (Kocher's test). This test, if positive, indicates an obstructed trachea. Kocher's test: Gentle compression on lateral lobes may produce stridor. This is due to narrow trachea. This test is particularly positive in multinodular goiters and carcinoma infiltrating into trachea which produce narrowed trachea. The position of the larynx and trachea should also be noted. This may be assessed by placing stethoscope on the suspected zone. Passage of air will indicate the position of the trachea. Simple palpation by an experienced hand will indicate the position of the trachea. Finally, X-ray may be advised to know the exact position of the trachea. Narrowing of the trachea, ie, 'Scabbard trachea becomes quite obvious in skiagram. The carotid sheath may be pushed backward by a benign swelling of the thyroid gland where the pulsation of the carotid artery may be felt (Fig. 27.14). A malignant thyroid may engulf the carotid sheath completely and pulsation of the artery cannot be felt. Sympathetic trunk may also be affected by thyroid swelling. This will lead to Horner's syndrome, i.e., slight sinking of the eyeball into the orbit (enophthalmos), slight drooping of the upper eyelid (pseudoptosis), contraction of the pupil (miosis) and absence of sweating of the affected side of the face (anhidrosis). Obstruction to the major veins in the thorax causes engorgement of neck veins are not uncommon. This sign becomes obvious when the patients are asked to raise the hands above the head and the arms touch the ears. This is known as Pemberton's sign. (vi) Whether there is any toxic manifestation or not. Primary toxic thyroid is generally not enlarged whereas an enlarged thyroid or nodular thyroid with toxic manifestation is generally a case of secondary thyrotoxicosis. In this case the brunt of attack is generally borne by the cardiovascular system whereas in primary thyrotoxicosis it is the nervous system which is mainly affected. ( vii) Whether there is any evidence of myxedema or not. (viii) Whether the swelling is a malignant one or a benign one. Is there any pulsation or thrill in the thyroid? of thyroid. Occasionally only cervical lymph nodes may be palpable, while the thyroid gland (x) Palpation of cervical lymph nodes: This is extremely important particularly in malignancy remains impalpable. Papillary carcinoma of thyroid is notorious for early lymphatic metastasis when the primary tumor remains quite small. Such enlargement was called 'aberrant thyroid previously, which is nothing but metastatic enlarged lymph nodes. manubrium sterni to exclude the presence Percussion: This is employed over the of a retrosterna goiter. This is more of theoretical importance rather than practical. Auscultation: In primary toxic goiter a systolic bruit (Figs. 27.15 and 27.16) may be heard over the goiter due to increased vascularity. Measurement of the circumference of the neck at the most prominent part of the swelling may be taken at intervals. This will determine whether the swelling is increasing or decreasing in size.
| Type | Examples |
|---|---|
| Uniform enlargement | Physiological goiter, colloid goiter, Hashimoto's disease |
| Isolated nodules | Nodular goiter |
| Lateral neck swelling | May be lymph node metastasis from hidden thyroid carcinoma (not aberrant thyroid) |
| Structure Compressed | Effect |
|---|---|
| Trachea/Larynx | Stridor, dyspnea |
| Esophagus | Dysphagia |
| Recurrent laryngeal nerve | Hoarseness of voice |
| Carotid sheath (benign) | Carotid pulsation felt posteriorly |
| Carotid sheath (malignant) | Carotid pulsation absent (engulfed) |
| Sympathetic trunk | Horner's syndrome: enophthalmos, pseudoptosis, miosis, anhidrosis |
| Major thoracic veins | Engorgement of neck veins; Pemberton's sign positive |
| Test/Sign | Description | Significance |
|---|---|---|
| Pizzillo's method | Hands behind head, push back | Improves inspection |
| Lahey's method | Push gland to one side, palpate opposite lobe | Better lobe palpation |
| Crile's method | Thumb on gland during swallowing | Detects subtle enlargement |
| Kocher's test | Compress lateral lobes | Stridor = narrowed trachea |
| Pemberton's sign | Arms raised above head | Congestion = retrosternal goiter |
Can u send it by making flowcahrts of different methods prese t it in detail
mkdir -p /tmp/workspace/thyroid-exam && ls /tmp/workspace/thyroid-exam/
~/thyroid-exam/generate_pdf.py
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch
import matplotlib.patheffects as pe
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm, mm
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image as RLImage, PageBreak, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.platypus import HRFlowable
import os
OUT_DIR = "/tmp/workspace/thyroid-exam/"
IMG_DIR = OUT_DIR + "imgs/"
os.makedirs(IMG_DIR, exist_ok=True)
# ─── Color Palette ────────────────────────────────────────────────────────────
C_HEADER = "#1a3a5c" # dark navy
C_BOX = "#2471a3" # blue
C_BOX2 = "#1a8a6e" # teal
C_BOX3 = "#8e44ad" # purple
C_BOX4 = "#c0392b" # red
C_BOX5 = "#d68910" # amber
C_ARROW = "#2c3e50"
C_LIGHT = "#d6eaf8"
C_LIGHT2 = "#d5f5e3"
C_LIGHT3 = "#e8daef"
C_LIGHT4 = "#fadbd8"
C_LIGHT5 = "#fef9e7"
C_WHITE = "#ffffff"
C_TEXT = "#1c2833"
C_SUBTEXT = "#566573"
# ─── Helper: draw a rounded box ───────────────────────────────────────────────
def rbox(ax, x, y, w, h, text, bg, fg="#ffffff", fontsize=9, bold=False,
radius=0.04, wrap=True, border=None):
box = FancyBboxPatch((x - w/2, y - h/2), w, h,
boxstyle=f"round,pad=0.01,rounding_size={radius}",
facecolor=bg, edgecolor=border or bg,
linewidth=1.5, zorder=3)
ax.add_patch(box)
weight = "bold" if bold else "normal"
ax.text(x, y, text, ha="center", va="center", fontsize=fontsize,
color=fg, weight=weight, wrap=wrap, zorder=4,
multialignment="center",
bbox=dict(boxstyle="round,pad=0.01", fc="none", ec="none"))
def arrow(ax, x1, y1, x2, y2, color=C_ARROW, lw=1.5, style="-|>"):
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle=style, color=color,
lw=lw, connectionstyle="arc3,rad=0.0"))
def diamond(ax, cx, cy, w, h, text, bg, fg="#ffffff", fontsize=8.5):
xs = [cx, cx+w/2, cx, cx-w/2, cx]
ys = [cy+h/2, cy, cy-h/2, cy, cy+h/2]
ax.fill(xs, ys, color=bg, zorder=3)
ax.plot(xs, ys, color=bg, lw=1.5, zorder=4)
ax.text(cx, cy, text, ha="center", va="center", fontsize=fontsize,
color=fg, weight="bold", zorder=5, multialignment="center")
def save_fig(fig, name):
path = IMG_DIR + name
fig.savefig(path, dpi=160, bbox_inches="tight", facecolor="white")
plt.close(fig)
return path
# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 1 – OVERALL LOCAL EXAMINATION OVERVIEW
# ══════════════════════════════════════════════════════════════════════════════
def fc_overview():
fig, ax = plt.subplots(figsize=(14, 9))
ax.set_xlim(0, 14); ax.set_ylim(0, 9)
ax.axis("off")
ax.set_facecolor("#f7f9fc")
fig.patch.set_facecolor("#f7f9fc")
ax.text(7, 8.55, "LOCAL EXAMINATION OF THYROID GLAND",
ha="center", va="center", fontsize=14, color=C_HEADER,
weight="bold", style="italic")
ax.text(7, 8.2, "A Systematic Approach",
ha="center", va="center", fontsize=10, color=C_SUBTEXT)
# Main start node
rbox(ax, 7, 7.7, 3.2, 0.55, "START: Patient Seated / Positioned", C_HEADER, fontsize=9.5, bold=True)
arrow(ax, 7, 7.42, 7, 7.05)
# 5 branches
steps = [
(2.0, "A\nINSPECTION", C_BOX),
(4.5, "B\nPALPATION", C_BOX2),
(7.0, "C\nPERCUSSION", C_BOX3),
(9.5, "D\nAUSCULTATION", C_BOX4),
(12.0, "E\nMEASUREMENT", C_BOX5),
]
for bx, label, col in steps:
arrow(ax, 7, 7.05, bx, 6.6, color=col)
rbox(ax, bx, 6.3, 1.8, 0.6, label, col, fontsize=10, bold=True)
# Sub-bullets for each
subs = {
2.0: ["Pizzillo's Method", "Swelling type\n(uniform / nodular)", "Movement on\ndeglutition", "Thyroglossal cyst\nvs Thyroid", "Retrosternal signs\n& Pemberton's sign"],
4.5: ["Neck flexed", "From behind\n& front", "Lahey's Method", "Crile's Method", "10 Key Points\nto Note"],
7.0: ["Over manubrium\nsterni", "Detect retrosternal\ngoiter", "(Mainly theoretical)"],
9.5: ["Systolic bruit\nin toxic goiter", "Trachea position\nby stethoscope"],
12.0: ["Circumference\nat prominent part", "Serial measurements\nto track change"],
}
ycur = {bx: 5.8 for bx, _, _ in steps}
colors_map = {2.0: C_LIGHT, 4.5: C_LIGHT2, 7.0: C_LIGHT3, 9.5: C_LIGHT4, 12.0: C_LIGHT5}
fg_map = {2.0: C_BOX, 4.5: C_BOX2, 7.0: C_BOX3, 9.5: C_BOX4, 12.0: C_BOX5}
for bx, _, col in steps:
for txt in subs[bx]:
arrow(ax, bx, ycur[bx] - 0.1, bx, ycur[bx] - 0.45, color=fg_map[bx])
rbox(ax, bx, ycur[bx] - 0.6, 1.75, 0.42,
txt, colors_map[bx], fg=fg_map[bx], fontsize=7.5, border=fg_map[bx])
ycur[bx] -= 1.05
# End node
rbox(ax, 7, 0.45, 4.5, 0.5, "RECORD FINDINGS & FORM CLINICAL DIAGNOSIS", C_HEADER, fontsize=9.5, bold=True)
save_fig(fig, "fc_overview.png")
# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 2 – INSPECTION FLOWCHART
# ══════════════════════════════════════════════════════════════════════════════
def fc_inspection():
fig, ax = plt.subplots(figsize=(13, 17))
ax.set_xlim(0, 13); ax.set_ylim(0, 17)
ax.axis("off")
ax.set_facecolor("#f7f9fc")
fig.patch.set_facecolor("#f7f9fc")
ax.text(6.5, 16.6, "FLOWCHART: INSPECTION OF THYROID GLAND",
ha="center", fontsize=13, color=C_HEADER, weight="bold")
# ── Step 1
rbox(ax, 6.5, 16.0, 5.5, 0.55, "STEP 1: Position the Patient", C_HEADER, fontsize=10, bold=True)
arrow(ax, 6.5, 15.72, 6.5, 15.3)
rbox(ax, 6.5, 15.1, 8.0, 0.5,
"Normal neck - is thyroid visible? (Obese / short-necked → harder to inspect)",
C_BOX, fontsize=9)
arrow(ax, 6.5, 14.85, 6.5, 14.45)
diamond(ax, 6.5, 14.1, 3.2, 0.6, "Is thyroid\nvisible?", C_BOX, fontsize=9)
# YES branch → right
arrow(ax, 8.1, 14.1, 10.5, 14.1, color="green")
rbox(ax, 11.5, 14.1, 2.0, 0.5, "YES → Note\nswelling type", "#1a8a6e", fontsize=8.5)
# NO branch → down → Pizzillo
arrow(ax, 6.5, 13.8, 6.5, 13.4, color="#c0392b")
rbox(ax, 6.5, 13.15, 6.5, 0.45, "NO → Use PIZZILLO'S METHOD", C_BOX2, fontsize=9, bold=True)
rbox(ax, 6.5, 12.55, 8.5, 0.75,
"Patient: Hands behind head, push head\nbackwards against clasped hands on occiput",
C_LIGHT2, fg=C_BOX2, fontsize=8.5, border=C_BOX2)
arrow(ax, 6.5, 12.17, 6.5, 11.75)
# ── Step 2: Swelling Type
rbox(ax, 6.5, 11.52, 5.5, 0.55, "STEP 2: Classify Swelling Type", C_HEADER, fontsize=10, bold=True)
arrow(ax, 6.5, 11.24, 6.5, 10.85)
diamond(ax, 6.5, 10.5, 3.8, 0.6, "Uniform OR\nNodular?", C_BOX, fontsize=9)
# UNIFORM → left
arrow(ax, 4.6, 10.5, 2.5, 10.5, color=C_BOX2)
rbox(ax, 1.5, 10.5, 2.0, 0.55, "UNIFORM", C_BOX2, fontsize=9, bold=True)
subs_u = ["Physiological\nGoiter", "Colloid Goiter", "Hashimoto's\nDisease"]
for i, s in enumerate(subs_u):
arrow(ax, 1.5, 10.22 - i*0.9, 1.5, 9.85 - i*0.9, color=C_BOX2)
rbox(ax, 1.5, 9.7 - i*0.9, 1.9, 0.4, s, C_LIGHT2, fg=C_BOX2, fontsize=8, border=C_BOX2)
# NODULAR → right
arrow(ax, 8.4, 10.5, 10.5, 10.5, color=C_BOX3)
rbox(ax, 11.5, 10.5, 2.0, 0.55, "NODULAR", C_BOX3, fontsize=9, bold=True)
subs_n = ["Multinodular\nGoiter", "Lateral neck:\nLymph node\nmets from hidden\nthyroid ca."]
for i, s in enumerate(subs_n):
arrow(ax, 11.5, 10.22 - i*1.0, 11.5, 9.8 - i*1.0, color=C_BOX3)
rbox(ax, 11.5, 9.6 - i*1.0, 1.9, 0.5, s, C_LIGHT3, fg=C_BOX3, fontsize=8, border=C_BOX3)
# ── Step 3: Deglutition
arrow(ax, 6.5, 10.2, 6.5, 9.3)
rbox(ax, 6.5, 9.1, 5.5, 0.55, "STEP 3: Ask Patient to SWALLOW", C_HEADER, fontsize=10, bold=True)
arrow(ax, 6.5, 8.82, 6.5, 8.45)
diamond(ax, 6.5, 8.1, 4.0, 0.6, "Does swelling move\nUPWARDS?", C_BOX, fontsize=9)
# YES
arrow(ax, 8.5, 8.1, 10.5, 8.1, color="green")
rbox(ax, 11.5, 8.1, 2.3, 0.5, "YES: Likely\nTHYROID / related", "#1a8a6e", fontsize=8.5)
subs_yes = ["Thyroid goiter", "Thyroglossal cyst", "Subhyoid bursitis", "Prelaryngeal LN\nfixed to larynx"]
for i, s in enumerate(subs_yes):
arrow(ax, 11.5, 7.85 - i*0.75, 11.5, 7.55 - i*0.75, color="#1a8a6e")
rbox(ax, 11.5, 7.4 - i*0.75, 2.1, 0.38, s, C_LIGHT2, fg="#1a8a6e", fontsize=7.5, border="#1a8a6e")
# NO
arrow(ax, 4.5, 8.1, 2.5, 8.1, color="#c0392b")
rbox(ax, 1.5, 8.1, 2.2, 0.5, "NO: Movement\nlimited / fixed", C_BOX4, fontsize=8.5)
subs_no = ["Malignant\ninfiltration", "Chronic\nthyroiditis", "Retrosternal\ngoiter"]
for i, s in enumerate(subs_no):
arrow(ax, 1.5, 7.85 - i*0.75, 1.5, 7.55 - i*0.75, color=C_BOX4)
rbox(ax, 1.5, 7.4 - i*0.75, 2.1, 0.38, s, C_LIGHT4, fg=C_BOX4, fontsize=7.5, border=C_BOX4)
# ── Step 4: Thyroglossal cyst check
arrow(ax, 6.5, 7.8, 6.5, 7.0)
rbox(ax, 6.5, 6.78, 5.5, 0.55, "STEP 4: Protrude the TONGUE", C_HEADER, fontsize=10, bold=True)
arrow(ax, 6.5, 6.5, 6.5, 6.1)
diamond(ax, 6.5, 5.75, 4.2, 0.6, "Does swelling move UP\nwith tongue protrusion?", C_BOX, fontsize=9)
arrow(ax, 8.6, 5.75, 10.5, 5.75, color="green")
rbox(ax, 11.5, 5.75, 2.3, 0.65, "YES:\nTHYROGLOSSAL\nCYST\n(Pathognomonic)", "#1a8a6e", fontsize=8.5, bold=True)
arrow(ax, 4.4, 5.75, 2.3, 5.75, color="#c0392b")
rbox(ax, 1.4, 5.75, 2.3, 0.5, "NO:\nNot thyroglossal\ncyst", C_BOX4, fontsize=8.5)
# ── Step 5: Retrosternal
arrow(ax, 6.5, 5.45, 6.5, 4.85)
rbox(ax, 6.5, 4.65, 5.5, 0.55, "STEP 5: Check for RETROSTERNAL GOITER", C_HEADER, fontsize=10, bold=True)
arrow(ax, 6.5, 4.37, 6.5, 4.0)
rbox(ax, 6.5, 3.72, 9.0, 0.55,
"Ask patient to raise BOTH ARMS above head until they touch the ears\n(Maintain position for a while) — PEMBERTON'S SIGN",
C_BOX5, fontsize=8.5, bold=True)
arrow(ax, 6.5, 3.44, 6.5, 3.05)
diamond(ax, 6.5, 2.7, 4.5, 0.6, "Congestion of face\n+ distress appear?", C_BOX5, fontsize=8.5)
arrow(ax, 8.75, 2.7, 10.5, 2.7, color="#d68910")
rbox(ax, 11.5, 2.7, 2.4, 0.55, "POSITIVE:\nRetrosternal\nGoiter suspected", C_BOX5, fontsize=8.5, bold=True)
arrow(ax, 4.25, 2.7, 2.3, 2.7, color="#566573")
rbox(ax, 1.3, 2.7, 2.2, 0.5, "NEGATIVE:\nNo retrosternal\ngoiter", C_SUBTEXT, fontsize=8.5)
# Also: subcutaneous veins
rbox(ax, 6.5, 1.9, 9.0, 0.55,
"Also inspect: Dilated subcutaneous veins over upper anterior thorax\n→ Also suggests retrosternal goiter / great vein compression",
C_LIGHT4, fg=C_BOX4, fontsize=8.5, border=C_BOX4)
arrow(ax, 6.5, 1.62, 6.5, 1.2)
rbox(ax, 6.5, 1.0, 5.0, 0.45, "PROCEED TO PALPATION", C_HEADER, fontsize=10, bold=True)
save_fig(fig, "fc_inspection.png")
# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 3 – PALPATION FLOWCHART
# ══════════════════════════════════════════════════════════════════════════════
def fc_palpation():
fig, ax = plt.subplots(figsize=(14, 20))
ax.set_xlim(0, 14); ax.set_ylim(0, 20)
ax.axis("off")
ax.set_facecolor("#f7f9fc")
fig.patch.set_facecolor("#f7f9fc")
ax.text(7, 19.6, "FLOWCHART: PALPATION OF THYROID GLAND",
ha="center", fontsize=13, color=C_HEADER, weight="bold")
# Setup
rbox(ax, 7, 19.1, 5.5, 0.55, "SETUP: Patient seated, neck slightly FLEXED", C_HEADER, fontsize=10, bold=True)
arrow(ax, 7, 18.82, 7, 18.45)
# Method choice
diamond(ax, 7, 18.1, 4.5, 0.6, "Choose palpation\napproach", C_BOX, fontsize=9)
# From behind
arrow(ax, 4.75, 18.1, 2.5, 18.1, color=C_BOX2)
rbox(ax, 1.5, 18.1, 2.5, 0.7, "FROM BEHIND\n(Standard)", C_BOX2, fontsize=9, bold=True)
rbox(ax, 1.5, 17.3, 2.5, 0.85,
"Examiner behind patient\nThumbs on back of neck\n4 fingers on each lobe\n& isthmus",
C_LIGHT2, fg=C_BOX2, fontsize=8, border=C_BOX2)
# From front - Lahey
arrow(ax, 9.25, 18.1, 11.0, 18.1, color=C_BOX3)
rbox(ax, 12.0, 18.1, 2.5, 0.7, "LAHEY'S METHOD\n(From front)", C_BOX3, fontsize=9, bold=True)
rbox(ax, 12.0, 17.3, 2.5, 0.95,
"Examiner in front\nPush gland to LEFT\n→ palpate LEFT lobe\nwith right hand\n(vice versa)",
C_LIGHT3, fg=C_BOX3, fontsize=8, border=C_BOX3)
# Crile
arrow(ax, 7, 17.8, 7, 17.4)
rbox(ax, 7, 17.15, 5.5, 0.6, "CRILE'S METHOD (During swallowing)", C_BOX5, fontsize=9, bold=True)
rbox(ax, 7, 16.6, 7.5, 0.65,
"Thumb placed on thyroid while patient swallows\n→ Slight enlargement / nodules detected by movement",
C_LIGHT5, fg=C_BOX5, fontsize=8.5, border=C_BOX5)
arrow(ax, 7, 16.27, 7, 15.85)
# ── 10 Key Points
rbox(ax, 7, 15.6, 7.0, 0.6, "10 KEY POINTS TO NOTE DURING PALPATION", C_HEADER, fontsize=10.5, bold=True)
arrow(ax, 7, 15.3, 7, 14.9)
pts = [
("(i) Whole Gland Enlargement?",
C_BOX, C_LIGHT,
["Smooth surface → Primary thyrotoxicosis / Colloid goiter",
"Bosselated → Multinodular goiter",
"Firm → Primary thyrotoxicosis / Hashimoto's",
"Soft → Colloid goiter",
"Hard/variable → Riedel's thyroiditis / Carcinoma"]),
("(ii) Localized Swelling?",
C_BOX2, C_LIGHT2,
["Note: position, size, shape, extent, consistency",
"Cystic → often feels FIRM (high internal tension)",
"Calcified cyst → may feel HARD"]),
("(iii) Mobility?",
C_BOX3, C_LIGHT3,
["Check horizontal AND vertical planes",
"FIXITY → Malignant tumor OR chronic thyroiditis"]),
("(iv) Retrosternal Extension Test?",
C_BOX4, C_LIGHT4,
["Index finger on lower border",
"Patient swallows → gland moves up",
"Check if lower border has downward extension"]),
("(v) Pressure Effects?",
"#7d6608", "#fef9e7",
["Trachea/Larynx → Stridor, dyspnea",
"Esophagus → Dysphagia",
"Recurrent laryngeal nerve → Hoarseness",
"Carotid sheath (benign) → pulsation felt posteriorly",
"Carotid sheath (malignant) → pulsation ABSENT",
"Sympathetic trunk → Horner's syndrome",
"Veins → Pemberton's sign positive"]),
("(vi) Toxic Manifestation?",
C_BOX, C_LIGHT,
["Primary toxic: small gland, nervous system affected",
"Secondary toxic: enlarged/nodular, cardiovascular affected"]),
("(vii) Myxedema?",
C_BOX2, C_LIGHT2,
["Look for features of hypothyroidism"]),
("(viii) Malignant vs Benign?",
C_BOX3, C_LIGHT3,
["Hard, fixed, irregular → malignant features",
"Soft, mobile, smooth → benign features"]),
("(ix) Pulsation / Thrill?",
C_BOX4, C_LIGHT4,
["Present in primary toxic goiter (hypervascular)"]),
("(x) Cervical Lymph Nodes?",
"#6e2fa1", "#e8daef",
["Very important in malignancy",
"Papillary carcinoma → early lymphatic spread",
"Small primary + enlarged LN → 'aberrant thyroid'\n(actually metastatic nodes)"]),
]
ypos = 14.65
for label, bcol, lcol, subs in pts:
rbox(ax, 7, ypos, 11.0, 0.5, label, bcol, fontsize=9.5, bold=True)
ypos -= 0.65
for s in subs:
rbox(ax, 7, ypos, 10.8, 0.42, "• " + s, lcol, fg=C_TEXT, fontsize=8.5, border=bcol)
ypos -= 0.5
ypos -= 0.25
if ypos < 0.5:
break
# Kocher test
rbox(ax, 7, ypos - 0.1, 11.0, 0.55, "KOCHER'S TEST", C_BOX4, fontsize=10, bold=True)
ypos -= 0.7
rbox(ax, 7, ypos, 11.0, 0.55,
"Gently compress lateral lobes → STRIDOR produced = POSITIVE (narrowed trachea)",
C_LIGHT4, fg=C_BOX4, fontsize=9, border=C_BOX4)
ypos -= 0.65
rbox(ax, 7, ypos, 11.0, 0.42,
"Positive in: Multinodular goiter | Carcinoma infiltrating trachea ('Scabbard trachea' on X-ray)",
C_LIGHT4, fg=C_BOX4, fontsize=8.5, border=C_BOX4)
save_fig(fig, "fc_palpation.png")
# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 4 – PRESSURE EFFECTS DETAIL
# ══════════════════════════════════════════════════════════════════════════════
def fc_pressure():
fig, ax = plt.subplots(figsize=(13, 11))
ax.set_xlim(0, 13); ax.set_ylim(0, 11)
ax.axis("off")
ax.set_facecolor("#f7f9fc")
fig.patch.set_facecolor("#f7f9fc")
ax.text(6.5, 10.65, "FLOWCHART: PRESSURE EFFECTS OF THYROID SWELLING",
ha="center", fontsize=13, color=C_HEADER, weight="bold")
rbox(ax, 6.5, 10.1, 6.0, 0.55, "THYROID SWELLING (Enlargement)", C_HEADER, fontsize=10.5, bold=True)
structures = [
(1.0, "TRACHEA /\nLARYNX", C_BOX, ["Stridor\n(inspiratory noise)", "Dyspnea", "+Kocher's test"]),
(3.3, "ESOPHAGUS", C_BOX2, ["Dysphagia"]),
(5.5, "RECURRENT\nLARYNGEAL N.", C_BOX3, ["Hoarseness\nof voice"]),
(7.8, "CAROTID\nSHEATH", C_BOX4, ["Benign:\nCarotid pulsation\nfelt POSTERIORLY", "Malignant:\nCarotid pulsation\nABSENT (engulfed)"]),
(10.5, "SYMPATHETIC\nTRUNK", "#7d6608", ["HORNER'S\nSYNDROME\n(see below)"]),
]
for bx, name, col, effects in structures:
arrow(ax, 6.5, 9.82, bx, 9.3, color=col)
rbox(ax, bx, 9.05, 2.2, 0.55, name, col, fontsize=9, bold=True)
yy = 8.55
for eff in effects:
arrow(ax, bx, yy, bx, yy - 0.4, color=col)
rbox(ax, bx, yy - 0.6, 2.1, 0.42, eff,
col + "33" if len(col) == 7 else "#d5f5e3",
fg=col, fontsize=8.5, border=col)
yy -= 1.15
# Horner's syndrome box
rbox(ax, 6.5, 5.4, 9.5, 0.55, "HORNER'S SYNDROME (Sympathetic trunk compression)", "#7d6608", fontsize=10, bold=True)
horner = [
(2.5, "Enophthalmos", "Slight sinking\nof eyeball\ninto orbit"),
(5.0, "Pseudoptosis", "Slight drooping\nof upper eyelid"),
(7.8, "Miosis", "Contraction\nof pupil"),
(10.5, "Anhidrosis", "Absence of sweating\non affected side\nof face"),
]
for bx, title, desc in horner:
arrow(ax, 6.5, 5.12, bx, 4.7, color="#7d6608")
rbox(ax, bx, 4.45, 2.3, 0.55, title, "#7d6608", fontsize=9, bold=True)
arrow(ax, bx, 4.17, bx, 3.82, color="#7d6608")
rbox(ax, bx, 3.6, 2.2, 0.5, desc, "#fef9e7", fg="#7d6608", fontsize=8.5, border="#7d6608")
# Venous obstruction
rbox(ax, 6.5, 2.75, 9.5, 0.55, "GREAT VEIN OBSTRUCTION (Retrosternal extension)", C_BOX4, fontsize=10, bold=True)
vein_items = [
(3.0, "Neck vein\nengorgement"),
(6.5, "Pemberton's Sign\n(Arms raised → face\ncongestion + distress)"),
(10.5, "Dilated subcutaneous\nveins over upper\nanterior thorax"),
]
for bx, desc in vein_items:
arrow(ax, 6.5, 2.47, bx, 2.1, color=C_BOX4)
rbox(ax, bx, 1.82, 2.8, 0.5, desc, C_LIGHT4, fg=C_BOX4, fontsize=8.5, border=C_BOX4)
save_fig(fig, "fc_pressure.png")
# ══════════════════════════════════════════════════════════════════════════════
# FLOWCHART 5 – SPECIAL TESTS SUMMARY
# ══════════════════════════════════════════════════════════════════════════════
def fc_special_tests():
fig, ax = plt.subplots(figsize=(13, 10))
ax.set_xlim(0, 13); ax.set_ylim(0, 10)
ax.axis("off")
ax.set_facecolor("#f7f9fc")
fig.patch.set_facecolor("#f7f9fc")
ax.text(6.5, 9.65, "FLOWCHART: SPECIAL / EPONYMOUS TESTS",
ha="center", fontsize=13, color=C_HEADER, weight="bold")
ax.text(6.5, 9.25, "Local Examination of Thyroid",
ha="center", fontsize=10, color=C_SUBTEXT)
tests = [
# (x-center, name, what, how, positive finding, significance, color)
(2.2, "PIZZILLO'S\nMETHOD", "Inspection aid",
"Hands behind head;\npatient pushes head\nback against clasped hands",
"Thyroid swelling\nbecomes visible",
"Makes inspection\neasier in obese /\nshort-necked",
C_BOX),
(5.1, "LAHEY'S\nMETHOD", "Palpation",
"Examiner in front;\npush gland to one side\npalpate opposite lobe",
"Better palpation\nof each lobe",
"Detects nodules\nin single lobe",
C_BOX2),
(8.0, "CRILE'S\nMETHOD", "Palpation during\nswallowing",
"Thumb on thyroid\npatient swallows",
"Movement felt\nunder thumb",
"Detects slight\nenlargement /<br/>small nodules",
C_BOX3),
(10.9, "KOCHER'S\nTEST", "Tracheal compression",
"Gentle lateral\ncompression of\nboth lobes",
"STRIDOR\nproduced",
"Narrowed trachea\n(multinodular /\ncarcinoma)",
C_BOX4),
]
for bx, name, cat, how, positive, sig, col in tests:
# Title
rbox(ax, bx, 8.5, 2.3, 0.65, name, col, fontsize=10, bold=True)
labels = ["Category", "How to perform", "Positive finding", "Clinical significance"]
vals = [cat, how, positive, sig]
colors2 = [col+"44" if len(col)==7 else "#d5f5e3",
col+"22" if len(col)==7 else "#eaf7f0",
"#fadbd8", "#eaf0fb"]
yy = 8.0
for lbl, val, cc in zip(labels, vals, colors2):
rbox(ax, bx, yy - 0.15, 2.3, 0.38, lbl, col, fontsize=7.5, bold=True)
yy -= 0.48
# count lines
lines = val.count('\n') + 1
h = 0.38 * lines
rbox(ax, bx, yy - h/2 + 0.06, 2.3, max(0.4, h), val,
C_LIGHT if col==C_BOX else C_LIGHT2 if col==C_BOX2 else C_LIGHT3 if col==C_BOX3 else C_LIGHT4,
fg=col, fontsize=8.0, border=col)
yy -= (h + 0.22)
# Pemberton's Sign
rbox(ax, 6.5, 2.2, 11.5, 0.6, "PEMBERTON'S SIGN", C_BOX5, fontsize=11, bold=True)
rbox(ax, 6.5, 1.65, 11.5, 0.55,
"How: Patient raises BOTH arms above head until they touch the ears (held for a while)",
C_LIGHT5, fg=C_BOX5, fontsize=9, border=C_BOX5)
rbox(ax, 6.5, 1.12, 5.5, 0.5,
"POSITIVE: Congestion of face + distress",
C_BOX5, fontsize=9, bold=True)
rbox(ax, 6.5, 0.55, 8.0, 0.5,
"Significance: Retrosternal goiter causing obstruction of great veins at thoracic inlet",
C_LIGHT5, fg=C_BOX5, fontsize=8.5, border=C_BOX5)
save_fig(fig, "fc_special_tests.png")
# ══════════════════════════════════════════════════════════════════════════════
# BUILD PDF
# ══════════════════════════════════════════════════════════════════════════════
def build_pdf():
print("Generating flowchart images...")
fc_overview()
fc_inspection()
fc_palpation()
fc_pressure()
fc_special_tests()
print("All images generated.")
pdf_path = OUT_DIR + "Thyroid_Local_Examination_Flowcharts.pdf"
doc = SimpleDocTemplate(pdf_path, pagesize=A4,
topMargin=1.5*cm, bottomMargin=1.5*cm,
leftMargin=1.5*cm, rightMargin=1.5*cm)
styles = getSampleStyleSheet()
title_style = ParagraphStyle("title", parent=styles["Title"],
fontSize=18, textColor=colors.HexColor(C_HEADER),
spaceAfter=6, alignment=TA_CENTER)
sub_style = ParagraphStyle("sub", parent=styles["Normal"],
fontSize=11, textColor=colors.HexColor(C_SUBTEXT),
spaceAfter=12, alignment=TA_CENTER)
head_style = ParagraphStyle("head", parent=styles["Heading2"],
fontSize=13, textColor=colors.HexColor(C_HEADER),
spaceBefore=10, spaceAfter=6)
body_style = ParagraphStyle("body", parent=styles["Normal"],
fontSize=9.5, textColor=colors.HexColor(C_TEXT),
spaceAfter=4, leading=14)
W = A4[0] - 3*cm # usable width
def img(name, scale=1.0):
path = IMG_DIR + name
from PIL import Image as PILImage
pil = PILImage.open(path)
iw, ih = pil.size
aspect = ih / iw
w = W * scale
h = w * aspect
return RLImage(path, width=w, height=h)
story = []
# ── Cover ──
story.append(Spacer(1, 2*cm))
story.append(Paragraph("LOCAL EXAMINATION OF", title_style))
story.append(Paragraph("THYROID GLAND", title_style))
story.append(Spacer(1, 0.4*cm))
story.append(Paragraph("Detailed Flowcharts for Clinical Examination", sub_style))
story.append(Spacer(1, 0.3*cm))
story.append(HRFlowable(width="80%", thickness=2, color=colors.HexColor(C_HEADER)))
story.append(Spacer(1, 0.5*cm))
cover_items = [
("Flowchart 1", "Overview of Local Examination"),
("Flowchart 2", "Inspection — Step-by-Step"),
("Flowchart 3", "Palpation — Methods & 10 Key Points"),
("Flowchart 4", "Pressure Effects of Thyroid Swelling"),
("Flowchart 5", "Special / Eponymous Clinical Tests"),
]
for num, title in cover_items:
story.append(Paragraph(f"<b>{num}:</b> {title}", body_style))
story.append(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="80%", thickness=1, color=colors.HexColor(C_SUBTEXT)))
story.append(PageBreak())
# ── Pages ──
pages = [
("fc_overview.png", "Flowchart 1: Overview of Local Examination",
"This flowchart presents the five pillars of local thyroid examination in sequence: Inspection → Palpation → Percussion → Auscultation → Measurement. Each step is broken into its key components. Use this as a quick-reference overview before diving into each step in detail."),
("fc_inspection.png", "Flowchart 2: Inspection — Step-by-Step",
"Inspection proceeds through five stages: (1) Positioning, including Pizzillo's method for difficult cases; (2) Classifying the swelling as uniform or nodular; (3) Observing movement on deglutition — the cardinal sign of thyroid origin; (4) Testing tongue protrusion for thyroglossal cyst; and (5) Pemberton's sign for retrosternal goiter."),
("fc_palpation.png", "Flowchart 3: Palpation — Methods & 10 Key Points",
"Palpation can be performed from behind (standard), using Lahey's method (from the front), or Crile's method (thumb during swallowing). The examiner must systematically assess 10 key points including surface character, consistency, mobility, retrosternal extension, pressure effects, toxic features, myxedema, malignancy signs, pulsation/thrill, and cervical lymph nodes. Kocher's test for tracheal compression is performed here."),
("fc_pressure.png", "Flowchart 4: Pressure Effects of Thyroid Swelling",
"An enlarging thyroid can compress five main structures: the trachea/larynx (stridor, dyspnea), esophagus (dysphagia), recurrent laryngeal nerve (hoarseness), carotid sheath (pulsation changes), and the sympathetic trunk (Horner's syndrome). Great vein obstruction in retrosternal goiter manifests as neck vein engorgement, dilated chest wall veins, and a positive Pemberton's sign."),
("fc_special_tests.png", "Flowchart 5: Special / Eponymous Clinical Tests",
"Five named tests are used in thyroid examination: Pizzillo's method (inspection aid), Lahey's method (single-lobe palpation), Crile's method (swallowing palpation), Kocher's test (tracheal narrowing), and Pemberton's sign (retrosternal goiter / great vein obstruction). Each is shown with its technique, positive finding, and clinical significance."),
]
for fc_img, heading, desc in pages:
story.append(Paragraph(heading, head_style))
story.append(Paragraph(desc, body_style))
story.append(Spacer(1, 0.3*cm))
story.append(img(fc_img, scale=1.0))
story.append(PageBreak())
doc.build(story)
print(f"PDF saved: {pdf_path}")
return pdf_path
path = build_pdf()
print("DONE:", path)
cd /tmp/workspace/thyroid-exam && python generate_pdf.py 2>&1
~/thyroid-exam/generate_pdf.py
Successfully replaced 1 instance(s) of the string in '/tmp/workspace/thyroid-exam/generate_pdf.py'
cd /tmp/workspace/thyroid-exam && python generate_pdf.py 2>&1
Thyroid Local Examination Flowcharts
PDF Document · PDF
| Page | Flowchart | What's covered |
|---|---|---|
| 1 | Cover page | Table of contents |
| 2 | Overview | All 5 steps (Inspection → Palpation → Percussion → Auscultation → Measurement) with sub-branches |
| 3 | Inspection | Pizzillo's method, swelling classification (uniform/nodular), deglutition test, tongue protrusion test, Pemberton's sign - each as a decision diamond |
| 4 | Palpation | Lahey's method, Crile's method, standard approach, all 10 key palpation points, Kocher's test |
| 5 | Pressure Effects | All 5 compressed structures (trachea, esophagus, RLN, carotid, sympathetic trunk), Horner's syndrome breakdown, venous obstruction signs |
| 6 | Special Tests | Pizzillo, Lahey, Crile, Kocher, Pemberton - each with technique, positive finding, and clinical significance |