lung examination

Reading File
Finding Sources
Finding Sources
Searching Images

lung examination percussion auscultation breath sounds

Finding Sources
Finding Sources
Searching Images

chest auscultation zones respiratory system clinical examination

Reading File
Finding Sources
Reading File
Reading File
Reading File
Reading File
Reading File
Finding Sources
Finding Sources
Reading File
Finding Sources
Reading File
Reading File
Reading File
Searching Images

barrel chest hyperinflation COPD emphysema

This diagnostic image pair displays thoracic radiology findings in an 80-year-old male with Chronic Obstructive Pulmonary Disease (COPD). The left panel shows a frontal chest radiograph (X-ray) exhibiting hallmark signs of hyperinflation, including a widened anteroposterior diameter and flattened diaphragmatic domes. Both lung fields demonstrate sparse bronchovascular markings and increased lucency, indicative of diffuse emphysema. The right panel shows an axial CT scan of the thorax, further characterizing the pulmonary pathology. It reveals significant emphysematous destruction of the lung parenchyma with the presence of multiple bullae and areas of decreased attenuation. Additionally, subtle interstitial lesions are visible. The imaging illustrates the classic presentation of severe COPD, characterized by permanent enlargement of airspaces distal to the terminal bronchioles and associated structural changes to the chest wall often described as a barrel chest.

This diagnostic image pair displays thoracic radiology findings in an 80-year-old male with Chronic Obstructive Pulmonary Disease (COPD). The left panel shows a frontal chest radiograph (X-ray) exhibiting hallmark signs of hyperinflation, including a widened anteroposterior diameter and flattened diaphragmatic domes. Both lung fields demonstrate sparse bronchovascular markings and increased lucency, indicative of diffuse emphysema. The right panel shows an axial CT scan of the thorax, further characterizing the pulmonary pathology. It reveals significant emphysematous destruction of the lung parenchyma with the presence of multiple bullae and areas of decreased attenuation. Additionally, subtle interstitial lesions are visible. The imaging illustrates the classic presentation of severe COPD, characterized by permanent enlargement of airspaces distal to the terminal bronchioles and associated structural changes to the chest wall often described as a barrel chest.

This diagnostic image displays a two-panel chest roentgenogram (X-ray) from a 65-year-old male, highlighting radiographic features of severe pulmonary emphysema and chronic obstructive pulmonary disease (COPD). Panel A presents a postero-anterior (PA) view, while Panel B provides a lateral view. Key findings include significant hyperinflation of the lung fields with marked flattening of the hemidiaphragms (indicated by white arrows), which loss their normal convex shape. In Panel A, a yellow arrow identifies areas of increased translucency and attenuation of vascular markings consistent with emphysematous parenchymal changes. A red arrow highlights the decreased zone of apposition, representing the diminished area where the diaphragm sits parallel to the inner rib cage. Panel B further illustrates the flattened diaphragmatic contour and an increased retrosternal clear space, reflecting an increased anterior-posterior thoracic diameter (barrel chest). These visual markers are clinically significant as they correlate with the mechanical basis of Hoover's sign, where the horizontal orientation of the diaphragm causes inward rib cage retraction during inspiration.

This diagnostic image displays a two-panel chest roentgenogram (X-ray) from a 65-year-old male, highlighting radiographic features of severe pulmonary emphysema and chronic obstructive pulmonary disease (COPD). Panel A presents a postero-anterior (PA) view, while Panel B provides a lateral view. Key findings include significant hyperinflation of the lung fields with marked flattening of the hemidiaphragms (indicated by white arrows), which loss their normal convex shape. In Panel A, a yellow arrow identifies areas of increased translucency and attenuation of vascular markings consistent with emphysematous parenchymal changes. A red arrow highlights the decreased zone of apposition, representing the diminished area where the diaphragm sits parallel to the inner rib cage. Panel B further illustrates the flattened diaphragmatic contour and an increased retrosternal clear space, reflecting an increased anterior-posterior thoracic diameter (barrel chest). These visual markers are clinically significant as they correlate with the mechanical basis of Hoover's sign, where the horizontal orientation of the diaphragm causes inward rib cage retraction during inspiration.

Searching Images

pleural effusion consolidation pneumothorax percussion findings comparison

I now have comprehensive material. Let me compile the full response:

Lung Examination

A systematic lung examination follows four classical steps: inspection, palpation, percussion, and auscultation. The physical exam should begin the moment the patient walks in and must always be interpreted alongside the history.

1. History (Before Physical Exam)

The history should precede hands-on examination and focus on:
  • Dyspnea: onset (acute vs. chronic), triggers, positional changes (orthopnea suggests CHF; platypnea suggests hepatopulmonary syndrome), severity graded by activity level
  • Cough: duration, productive vs. dry, sputum color/quantity, blood-streaking (hemoptysis)
  • Wheezing: suggests airway disease (asthma, COPD)
  • Chest pain: pleuritic pain (sharp, worse with breathing) suggests pneumothorax or pleuritis
  • Risk factors: smoking history (pack-years), occupational exposures (dusts, fumes), family history (CF, α1-antitrypsin deficiency), medications
  • Harrison's Principles of Internal Medicine 22E, [Respiratory History section]

2. Inspection

Inspection starts at the bedside before touching the patient:
General Appearance
  • Can the patient speak in full sentences? Inability suggests significant respiratory compromise
  • Respiratory rate (normal 12-20/min): tachypnea indicates increased drive to breathe
  • Oxygen saturation by pulse oximetry - now considered a 5th vital sign
  • Central cyanosis (bluish tinge of lips/tongue): implies >5 g/dL of deoxyhaemoglobin
  • Clubbing: associated with chronic lung disease (cystic fibrosis, IPF, lung cancer, bronchiectasis)
Chest Wall
  • Barrel chest (increased AP diameter): hyperexpansion of COPD/emphysema
  • Pectus excavatum or pectus carinatum: can restrict breathing
  • Kyphoscoliosis: causes restrictive lung disease from extrapulmonary origin
  • Intercostal retractions (especially in children in respiratory distress)
  • Symmetry of chest movement: asymmetry suggests unilateral pathology
Breathing Pattern
  • Accessory muscle use (sternocleidomastoid, scalene muscles): indicates increased respiratory drive or impaired ventilatory pump
  • Paradoxical abdominal motion: suggests respiratory muscle weakness/fatigue
  • Hoover sign (inward motion of lower lateral rib cage on inspiration): hallmark of emphysema due to flattened diaphragm
  • Kussmaul breathing (deep breathing with mild tachypnea): compensating for severe metabolic acidosis
  • Pursed-lip breathing: seen in COPD to reduce dynamic airway collapse
Here is the classic CXR appearance of barrel chest and hyperinflation in COPD:
CXR showing barrel chest and hyperinflation with flattened diaphragms and increased retrosternal airspace in COPD/emphysema
  • Murray & Nadel's Textbook of Respiratory Medicine, Physical Examination section

3. Palpation

  • Tracheal position: central normally; shifted away from the affected side in large pleural effusion or tension pneumothorax; shifted toward the affected side in lobar collapse/atelectasis
  • Chest expansion: place hands on the lower posterior chest, thumbs meeting at the midline - symmetry is checked by how far thumbs are displaced on deep inspiration. Reduced unilateral expansion points to pleural effusion, pneumothorax, consolidation, or atelectasis on that side.
  • Tactile (vocal) fremitus: ask patient to repeat "ninety-nine" while palpating both sides simultaneously with the ulnar border of your hand or finger tips:
    • Increased fremitus = consolidation (pneumonia) - sound conducted better through solid tissue
    • Decreased/absent fremitus = pleural effusion, pneumothorax, or obstruction
  • Palpation for tenderness (e.g. costochondritis, fractured rib), subcutaneous emphysema (crepitus)

4. Percussion

Percussion of normal lung is resonant. Abnormal findings:
FindingCause
Dullness / FlatnessPleural effusion, consolidation (pneumonia), collapse
Hyperresonance / TympanyPneumothorax, emphysema/hyperinflation
Stony dullnessLarge pleural effusion
  • Compare the same level on both sides of the chest (posterior, lateral, anterior)
  • Map the lower border of the lung (normally at T10 posteriorly on deep inspiration)
  • Diaphragmatic excursion: percuss during held inspiration then held expiration to measure movement (normally 3-5 cm)

5. Auscultation

Listen with a stethoscope over posterior (upper, middle, lower zones), lateral, and anterior fields. The right middle lobe and left lingula are audible only anteriorly.

Normal Breath Sounds

TypeLocationCharacter
VesicularPeripheral lungSoft, low-pitched, continuous; inspiration longer than expiration
Bronchial (tubular)Over tracheaLoud, high-pitched; expiration longer than inspiration
BronchovesicularMain bronchi (sternal angle area)Intermediate
Bronchial breath sounds heard in the peripheral lung fields indicate consolidation (pathological).

Added (Adventitious) Sounds

SoundDescriptionAssociation
Crackles (rales)Discontinuous, popping soundsFine crackles at bases: pulmonary edema; fine "Velcro" crackles: IPF; coarse crackles: pneumonia, bronchiectasis
WheezesContinuous, high-pitched, musical, usually expiratoryAsthma, COPD (airway narrowing)
RhonchiLow-pitched, continuousMucus in large airways; bronchitis, bronchiectasis
StridorHigh-pitched, monophonic inspiratory sound, louder over neckUpper airway obstruction (laryngeal edema, foreign body)
Pleural rubCreaking or leathery sound synchronous with breathingPleuritis; disappears when significant effusion develops

Voice Transmission Tests

These differentiate consolidation (increased transmission) from effusion (decreased):
  • Bronchophony: ask the patient to say "99" - louder transmission over consolidation
  • Whispered pectoriloquy: whisper "1-2-3" - clearly heard over consolidation, absent over effusion
  • Egophony: ask patient to say "EEE" - sounds like "AAY" over consolidation (also at the top edge of a pleural effusion)

Decreased/Absent Breath Sounds

  • Diffusely decreased: emphysema (quiet chest)
  • Unilaterally absent: pneumothorax, large pleural effusion

6. Summary: Physical Findings in Common Disorders

Adapted from Textbook of Family Medicine 9e, Table 16-2:
DisorderInspectionPalpationPercussionAuscultation
Asthma (acute)Hyperinflation; accessory musclesImpaired expansion; decreased fremitusHyperresonance; low diaphragmProlonged expiration; inspiratory + expiratory wheeze
PneumothoraxLag on affected sideAbsent fremitusHyperresonant / tympaniticAbsent breath sounds
Pleural effusionLag on affected sideDecreased fremitus; trachea shifted awayStony dullnessAbsent breath sounds
AtelectasisLag on affected sideDecreased fremitus; trachea shifted towardDullnessAbsent breath sounds
Consolidation (pneumonia)Possible splintingIncreased fremitusDullnessBronchial sounds; bronchophony; egophony; crackles
Emphysema/COPDBarrel chest; Hoover signDecreased expansionHyperresonanceDecreased vesicular; prolonged expiration; possible wheeze

7. Signs of Respiratory Distress & Systemic Associations

  • Pulsus paradoxus (>10 mmHg fall in systolic BP on inspiration): severe asthma, pericardial tamponade
  • Jugular venous distension + peripheral edema: cor pulmonale (right heart failure from chronic lung disease)
  • Skin findings: erythema nodosum (sarcoidosis), Raynaud phenomenon (scleroderma-associated ILD)
  • Joint findings: rheumatologic disease can manifest primarily as lung disease

Sources:
  • Harrison's Principles of Internal Medicine 22E (2025), Respiratory History and Physical Examination
  • Murray & Nadel's Textbook of Respiratory Medicine, Physical Examination Chapter
  • Textbook of Family Medicine 9e, Chapter 16
  • Goldman-Cecil Medicine, History and Physical Examination

Create a quick reference PDF guide for lung exam findings

Reading File
Reading File
Running Command

ls -la /tmp/workspace/ 2>/dev/null || echo "workspace empty or missing"

Running Command

mkdir -p /tmp/workspace/lung-exam-pdf

Writing File

~/lung-exam-pdf/build_pdf.py

from reportlab.platypus import (
    SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer,
    HRFlowable, KeepTogether
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm, mm
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.platypus import PageBreak

OUTPUT = "/tmp/workspace/lung-exam-pdf/Lung_Exam_Quick_Reference.pdf"

# ── Colour palette ──────────────────────────────────────────────────────────
C_DARK_BLUE   = colors.HexColor("#1a3a5c")
C_MID_BLUE    = colors.HexColor("#2a6496")
C_LIGHT_BLUE  = colors.HexColor("#d6eaf8")
C_ACCENT      = colors.HexColor("#e8f4fd")
C_ORANGE      = colors.HexColor("#e67e22")
C_GREEN       = colors.HexColor("#1e8449")
C_RED         = colors.HexColor("#c0392b")
C_HEADER_ROW  = colors.HexColor("#1a3a5c")
C_ALT_ROW     = colors.HexColor("#eaf4fb")
C_WHITE       = colors.white
C_LIGHT_GREY  = colors.HexColor("#f4f6f7")
C_BORDER      = colors.HexColor("#85929e")

PAGE_W, PAGE_H = A4
MARGIN = 1.8 * cm

# ── Document ─────────────────────────────────────────────────────────────────
doc = SimpleDocTemplate(
    OUTPUT,
    pagesize=A4,
    leftMargin=MARGIN, rightMargin=MARGIN,
    topMargin=MARGIN, bottomMargin=MARGIN,
    title="Lung Examination Quick Reference",
    author="Orris Medical"
)

styles = getSampleStyleSheet()
W = PAGE_W - 2 * MARGIN   # usable width

# ── Custom styles ─────────────────────────────────────────────────────────────
def S(name, **kw):
    return ParagraphStyle(name, **kw)

title_style = S("Title2",
    fontName="Helvetica-Bold", fontSize=22,
    textColor=C_WHITE, alignment=TA_CENTER,
    spaceAfter=4, leading=28)

subtitle_style = S("Subtitle2",
    fontName="Helvetica", fontSize=11,
    textColor=colors.HexColor("#d6eaf8"), alignment=TA_CENTER,
    spaceAfter=2)

section_style = S("Section",
    fontName="Helvetica-Bold", fontSize=12,
    textColor=C_WHITE, backColor=C_DARK_BLUE,
    leftIndent=6, rightIndent=6,
    spaceBefore=10, spaceAfter=4, leading=16,
    borderPad=4)

sub_section_style = S("SubSection",
    fontName="Helvetica-Bold", fontSize=10,
    textColor=C_MID_BLUE,
    spaceBefore=6, spaceAfter=2)

body_style = S("Body2",
    fontName="Helvetica", fontSize=9,
    leading=13, spaceAfter=3,
    textColor=colors.HexColor("#1c2833"))

bullet_style = S("Bullet2",
    fontName="Helvetica", fontSize=9,
    leading=13, leftIndent=12, spaceAfter=2,
    textColor=colors.HexColor("#1c2833"))

cell_style = S("Cell",
    fontName="Helvetica", fontSize=8.5,
    leading=12, textColor=colors.HexColor("#1c2833"))

cell_bold = S("CellBold",
    fontName="Helvetica-Bold", fontSize=8.5,
    leading=12, textColor=colors.HexColor("#1c2833"))

cell_header = S("CellHeader",
    fontName="Helvetica-Bold", fontSize=9,
    leading=13, textColor=C_WHITE, alignment=TA_CENTER)

caption_style = S("Caption",
    fontName="Helvetica-Oblique", fontSize=7.5,
    textColor=C_BORDER, spaceAfter=6)

note_style = S("Note",
    fontName="Helvetica-Oblique", fontSize=8,
    textColor=colors.HexColor("#5d6d7e"),
    spaceAfter=4)

# ── Helpers ───────────────────────────────────────────────────────────────────
def section_header(text):
    tbl = Table([[Paragraph(f"  {text}", section_style)]], colWidths=[W])
    tbl.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), C_DARK_BLUE),
        ("ROWBACKGROUNDS", (0,0), (-1,-1), [C_DARK_BLUE]),
        ("TOPPADDING",  (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING", (0,0), (-1,-1), 8),
        ("ROUNDEDCORNERS", [3,3,3,3]),
    ]))
    return tbl

def mini_header(text, color=C_MID_BLUE):
    return Paragraph(f"<font color='#{color.hexval()[2:]}'>■</font>  <b>{text}</b>", sub_section_style)

def hr():
    return HRFlowable(width=W, thickness=0.5, color=C_BORDER, spaceAfter=4, spaceBefore=2)

def make_table(data, col_widths, alt=True, header_bg=C_HEADER_ROW, font_size=8.5):
    """data: list of lists (first row = header)"""
    parsed = []
    for ri, row in enumerate(data):
        parsed_row = []
        for ci, cell in enumerate(row):
            if isinstance(cell, str):
                if ri == 0:
                    parsed_row.append(Paragraph(cell, cell_header))
                else:
                    parsed_row.append(Paragraph(cell, cell_style))
            else:
                parsed_row.append(cell)
        parsed.append(parsed_row)

    t = Table(parsed, colWidths=col_widths, repeatRows=1)

    style_cmds = [
        ("BACKGROUND",    (0,0), (-1,0),  header_bg),
        ("TEXTCOLOR",     (0,0), (-1,0),  C_WHITE),
        ("FONTNAME",      (0,0), (-1,0),  "Helvetica-Bold"),
        ("FONTSIZE",      (0,0), (-1,-1), font_size),
        ("ALIGN",         (0,0), (-1,0),  "CENTER"),
        ("ALIGN",         (0,1), (-1,-1), "LEFT"),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [C_WHITE, C_ALT_ROW] if alt else [C_WHITE]),
        ("GRID",          (0,0), (-1,-1), 0.4, C_BORDER),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 5),
        ("RIGHTPADDING",  (0,0), (-1,-1), 5),
    ]
    t.setStyle(TableStyle(style_cmds))
    return t

def colored_cell(text, bg, fg=C_WHITE, bold=True):
    st = S("cc", fontName="Helvetica-Bold" if bold else "Helvetica",
           fontSize=8.5, textColor=fg, alignment=TA_CENTER, leading=12)
    t = Table([[Paragraph(text, st)]], colWidths=[None])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("TOPPADDING",  (0,0), (-1,-1), 3),
        ("BOTTOMPADDING",(0,0), (-1,-1), 3),
        ("LEFTPADDING",  (0,0), (-1,-1), 4),
        ("RIGHTPADDING", (0,0), (-1,-1), 4),
    ]))
    return t

# ── TITLE BLOCK ───────────────────────────────────────────────────────────────
def title_block():
    title_tbl = Table(
        [[Paragraph("LUNG EXAMINATION", title_style)],
         [Paragraph("Quick Reference Guide", subtitle_style)],
         [Paragraph("Harrison's · Murray &amp; Nadel · Goldman-Cecil · Textbook of Family Medicine", caption_style)]],
        colWidths=[W]
    )
    title_tbl.setStyle(TableStyle([
        ("BACKGROUND",    (0,0), (-1,-1), C_DARK_BLUE),
        ("TOPPADDING",    (0,0), (-1,-1), 10),
        ("BOTTOMPADDING", (0,0), (-1,-1), 8),
        ("LEFTPADDING",   (0,0), (-1,-1), 10),
        ("RIGHTPADDING",  (0,0), (-1,-1), 10),
        ("ROUNDEDCORNERS",[5,5,5,5]),
    ]))
    return title_tbl

# ── PAGE 1: INSPECT / PALPATE / PERCUSS ──────────────────────────────────────
def page1():
    els = []
    els.append(title_block())
    els.append(Spacer(1, 8))

    # ── 1. INSPECTION ──────────────────────────────────────────────
    els.append(section_header("1  INSPECTION"))
    els.append(Spacer(1, 4))

    # Two-column layout: General | Chest Wall
    col_w = W / 2 - 3
    gen_data = [
        ["GENERAL SIGNS"],
        ["Tachypnea (>20/min) — increased drive"],
        ["Central cyanosis — >5 g/dL deoxy-Hb"],
        ["Clubbing — IPF, lung ca., CF, bronchiectasis"],
        ["Pursed-lip breathing — COPD"],
        ["3-4 word dyspnea — severe limitation"],
    ]
    chest_data = [
        ["CHEST WALL"],
        ["Barrel chest (↑AP diameter) — emphysema"],
        ["Pectus excavatum / carinatum — restriction"],
        ["Kyphoscoliosis — extrapulmonary restriction"],
        ["Intercostal retractions — ↑ work of breathing"],
        ["Lag on one side — unilateral pathology"],
    ]

    def mini_box(rows, header_col):
        trows = []
        for i, r in enumerate(rows):
            bg = header_col if i == 0 else (C_ALT_ROW if i % 2 == 0 else C_WHITE)
            fg = C_WHITE if i == 0 else colors.HexColor("#1c2833")
            fn = "Helvetica-Bold" if i == 0 else "Helvetica"
            trows.append([Paragraph(r[0],
                ParagraphStyle("mb", fontName=fn, fontSize=8.5,
                               textColor=fg, leading=12))])
        t = Table(trows, colWidths=[col_w])
        cmds = [
            ("GRID",          (0,0), (-1,-1), 0.4, C_BORDER),
            ("TOPPADDING",    (0,0), (-1,-1), 3),
            ("BOTTOMPADDING", (0,0), (-1,-1), 3),
            ("LEFTPADDING",   (0,0), (-1,-1), 6),
        ]
        for i in range(len(rows)):
            bg = header_col if i == 0 else (C_ALT_ROW if i % 2 == 0 else C_WHITE)
            cmds.append(("BACKGROUND", (0,i), (-1,i), bg))
        t.setStyle(TableStyle(cmds))
        return t

    two_col = Table(
        [[mini_box(gen_data, C_MID_BLUE), mini_box(chest_data, C_GREEN)]],
        colWidths=[col_w + 3, col_w + 3]
    )
    two_col.setStyle(TableStyle([
        ("VALIGN", (0,0), (-1,-1), "TOP"),
        ("LEFTPADDING",  (0,0), (-1,-1), 0),
        ("RIGHTPADDING", (0,0), (-1,-1), 0),
        ("TOPPADDING",   (0,0), (-1,-1), 0),
        ("BOTTOMPADDING",(0,0), (-1,-1), 0),
    ]))
    els.append(two_col)

    # Breathing pattern sub-box
    els.append(Spacer(1, 6))
    els.append(mini_header("Breathing Patterns"))
    bp_data = [
        ["Sign", "Description", "Cause"],
        ["Accessory muscle use", "SCM, scalene activation on breathing", "COPD, severe asthma, resp. muscle weakness"],
        ["Hoover sign", "Inward lower lateral rib cage on inspiration", "Emphysema (flattened diaphragm)"],
        ["Paradoxical abdomen", "Abdomen moves inward on inspiration", "Diaphragmatic weakness / fatigue"],
        ["Kussmaul breathing", "Deep rapid breaths", "Severe metabolic acidosis"],
        ["Abdominal rounding", "Periumbilical outward on exhalation", "Acute pulmonary oedema (↑ PEEP attempt)"],
    ]
    els.append(make_table(bp_data, [3.5*cm, 5.5*cm, 6.5*cm]))

    els.append(Spacer(1, 8))

    # ── 2. PALPATION ───────────────────────────────────────────────
    els.append(section_header("2  PALPATION"))
    els.append(Spacer(1, 4))

    palp_data = [
        ["Finding", "Normal", "Abnormal — Shifted TOWARD affected side", "Abnormal — Shifted AWAY"],
        ["Trachea", "Central", "Collapse / Atelectasis", "Large effusion · Tension pneumothorax"],
        ["Apex beat", "5th ICS, MCL", "Collapse / Fibrosis", "Large effusion"],
        ["Chest expansion", "Symmetrical", "Ipsilateral pathology", "—"],
        ["Tactile fremitus", "Present / equal", "↑ = Consolidation", "↓/Absent = Effusion, Pneumothorax, Obstruction"],
    ]
    els.append(make_table(palp_data, [3.2*cm, 2.8*cm, 5.5*cm, 4*cm], header_bg=C_GREEN))

    els.append(Spacer(1, 8))

    # ── 3. PERCUSSION ──────────────────────────────────────────────
    els.append(section_header("3  PERCUSSION"))
    els.append(Spacer(1, 4))

    perc_data = [
        ["Note", "Quality", "Conditions"],
        ["Resonant", "Normal lung sound", "Normal aerated lung"],
        ["Dullness", "Thud-like, flat", "Consolidation (pneumonia), Pleural effusion, Collapse"],
        ["Stony dullness", "Extreme flatness", "Large pleural effusion"],
        ["Hyperresonance", "Drum-like, louder", "Pneumothorax, Emphysema / severe asthma"],
        ["Tympany", "Hollow, musical", "Large pneumothorax, Gastric bubble (left base)"],
    ]
    color_map = {1: C_LIGHT_BLUE, 2: C_RED, 3: colors.HexColor("#fadbd8"),
                 4: colors.HexColor("#d5f5e3"), 5: colors.HexColor("#fef9e7")}
    els.append(make_table(perc_data, [3.2*cm, 3.8*cm, 8.5*cm], header_bg=C_ORANGE))

    els.append(Spacer(1, 6))
    els.append(Paragraph(
        "<b>Technique:</b>  Place middle finger of non-dominant hand flat on the chest — strike the middle phalanx "
        "sharply with the tip of the dominant middle finger. Compare symmetrically. Map diaphragm by percussing "
        "from resonance to dullness (normally at T10 posteriorly); measure excursion (normal 3-5 cm).",
        note_style))

    return els

# ── PAGE 2: AUSCULTATION + COMPARISON TABLE ───────────────────────────────────
def page2():
    els = []
    els.append(PageBreak())

    # ── 4. AUSCULTATION ────────────────────────────────────────────
    els.append(section_header("4  AUSCULTATION"))
    els.append(Spacer(1, 4))

    els.append(mini_header("Normal Breath Sounds"))
    norm_data = [
        ["Sound", "Location", "Inspiration : Expiration", "Character"],
        ["Vesicular", "Peripheral lung fields", "I > E  (3:1)", "Soft, low-pitched, continuous"],
        ["Bronchovesicular", "1st/2nd ICS anteriorly, between scapulae", "I = E  (1:1)", "Medium pitch, medium intensity"],
        ["Bronchial (tubular)", "Over trachea / manubrium", "E > I  (2:3)", "Loud, high-pitched, gap between I and E"],
    ]
    els.append(make_table(norm_data, [3.2*cm, 5.0*cm, 3.2*cm, 4.1*cm]))

    els.append(Spacer(1, 6))
    els.append(Paragraph(
        "<b>Note:</b>  Bronchial breath sounds heard in the <b>peripheral</b> lung fields = pathological "
        "(indicates consolidation beneath the stethoscope).",
        note_style))

    els.append(Spacer(1, 6))
    els.append(mini_header("Added (Adventitious) Sounds"))
    adv_data = [
        ["Sound", "Type", "Character", "Key Associations"],
        ["Fine crackles", "Discontinuous", "Soft, high-pitched, end-inspiratory; like Velcro or hair rubbed near ear",
         "Pulmonary oedema (basal), IPF (bibasal Velcro), early pneumonia"],
        ["Coarse crackles", "Discontinuous", "Loud, lower-pitched, inspiratory ± expiratory",
         "Bronchiectasis, bronchitis, resolving pneumonia"],
        ["Wheeze", "Continuous", "Musical, high-pitched, mainly expiratory",
         "Asthma, COPD, bronchospasm (any cause)"],
        ["Rhonchi", "Continuous", "Low-pitched, snoring quality",
         "Secretions in large airways; bronchitis, COPD"],
        ["Stridor", "Continuous", "High-pitched, monophonic, mainly inspiratory, loudest over neck",
         "Upper airway obstruction (foreign body, laryngeal oedema, croup)"],
        ["Pleural rub", "Discontinuous", "Creaking / leathery, both phases, not cleared by coughing",
         "Pleuritis; disappears when large effusion develops"],
    ]
    els.append(make_table(adv_data, [2.5*cm, 2.6*cm, 5.0*cm, 5.4*cm]))

    els.append(Spacer(1, 6))
    els.append(mini_header("Vocal Resonance Tests"))
    vr_data = [
        ["Test", "Technique", "Consolidation", "Pleural Effusion"],
        ["Bronchophony", "Say '99' — auscultate", "Louder, clearer", "Absent / muffled"],
        ["Whispered pectoriloquy", "Whisper '1-2-3' — auscultate", "Clearly transmitted", "Absent"],
        ["Egophony", "Say 'EEE' — auscultate", "Sounds like 'AAY'", "Absent (except top edge of effusion)"],
    ]
    els.append(make_table(vr_data, [3.8*cm, 4.0*cm, 3.5*cm, 4.2*cm]))

    els.append(Spacer(1, 10))

    # ── 5. COMPARISON TABLE ────────────────────────────────────────
    els.append(section_header("5  CLINICAL FINDINGS COMPARISON TABLE"))
    els.append(Spacer(1, 4))

    comp_data = [
        ["Disorder", "Inspection", "Trachea", "Fremitus", "Percussion", "Breath Sounds", "Added Sounds"],
        ["Consolidation\n(Pneumonia)",
         "Possible splinting / lag",
         "Central",
         "↑ Increased",
         "Dull",
         "Bronchial (pathological)",
         "Crackles, egophony, bronchophony"],
        ["Pleural Effusion\n(large)",
         "Lag on affected side",
         "Shifted AWAY",
         "↓ Decreased",
         "Stony dull",
         "Absent",
         "Pleural rub (early); egophony at top edge"],
        ["Pneumothorax\n(complete)",
         "Lag on affected side",
         "Shifted AWAY\n(tension)",
         "Absent",
         "Hyperresonant / tympanitic",
         "Absent",
         "None"],
        ["Atelectasis\n(lobar collapse)",
         "Lag on affected side",
         "Shifted TOWARD",
         "↓ Decreased",
         "Dull",
         "Absent / reduced",
         "None"],
        ["Asthma\n(acute attack)",
         "Hyperinflation;\naccessory muscles",
         "Central",
         "↓ Decreased",
         "Hyperresonant;\nlow diaphragm",
         "Prolonged expiration;\nreduced",
         "Expiratory + inspiratory wheeze"],
        ["COPD /\nEmphysema",
         "Barrel chest;\nHoover sign;\npursed-lip breathing",
         "Central",
         "↓ Decreased",
         "Hyperresonant",
         "Globally ↓ (quiet chest)",
         "Wheeze; prolonged expiration"],
        ["IPF\n(Pulmonary Fibrosis)",
         "Clubbing; cyanosis\n(advanced)",
         "Central",
         "Normal / ↑",
         "Resonant",
         "Vesicular (reduced volume)",
         "Fine bibasal Velcro crackles"],
    ]

    # Build with wrapped paragraphs
    cw = [3.0*cm, 2.8*cm, 2.2*cm, 2.0*cm, 2.5*cm, 3.0*cm, 4.0*cm]
    parsed = []
    for ri, row in enumerate(comp_data):
        pr = []
        for ci, cell in enumerate(row):
            if ri == 0:
                pr.append(Paragraph(cell, cell_header))
            else:
                pr.append(Paragraph(cell.replace("\n", "<br/>"),
                    ParagraphStyle("ctab", fontName="Helvetica", fontSize=7.8,
                                   leading=11, textColor=colors.HexColor("#1c2833"))))
        parsed.append(pr)

    t = Table(parsed, colWidths=cw, repeatRows=1)
    style_cmds = [
        ("BACKGROUND",    (0,0), (-1,0),  C_DARK_BLUE),
        ("TEXTCOLOR",     (0,0), (-1,0),  C_WHITE),
        ("FONTNAME",      (0,0), (-1,0),  "Helvetica-Bold"),
        ("ALIGN",         (0,0), (-1,0),  "CENTER"),
        ("ALIGN",         (0,1), (-1,-1), "LEFT"),
        ("VALIGN",        (0,0), (-1,-1), "TOP"),
        ("GRID",          (0,0), (-1,-1), 0.4, C_BORDER),
        ("TOPPADDING",    (0,0), (-1,-1), 4),
        ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("LEFTPADDING",   (0,0), (-1,-1), 4),
        ("RIGHTPADDING",  (0,0), (-1,-1), 4),
        # Alternate row colours
        ("ROWBACKGROUNDS",(0,1), (-1,-1), [C_WHITE, C_ALT_ROW]),
        # Highlight specific percussion findings
        ("BACKGROUND",    (4,1), (4,1), colors.HexColor("#fdecea")),  # dull - pneumonia
        ("BACKGROUND",    (4,2), (4,2), colors.HexColor("#fdecea")),  # dull - effusion
        ("BACKGROUND",    (4,4), (4,4), colors.HexColor("#d5f5e3")),  # hyper - asthma
        ("BACKGROUND",    (4,5), (4,5), colors.HexColor("#d5f5e3")),  # hyper - COPD
    ]
    t.setStyle(TableStyle(style_cmds))
    els.append(t)

    return els

# ── PAGE 3: SPECIAL SIGNS + QUICK TIPS ───────────────────────────────────────
def page3():
    els = []
    els.append(PageBreak())

    # ── 6. IMPORTANT CLINICAL SIGNS ────────────────────────────────
    els.append(section_header("6  IMPORTANT CLINICAL SIGNS"))
    els.append(Spacer(1, 4))

    signs_data = [
        ["Sign", "Definition", "Clinical Significance"],
        ["Pulsus paradoxus\n(>10 mmHg)", "Systolic BP falls >10 mmHg on inspiration",
         "Severe asthma / COPD (impending resp. failure); pericardial tamponade"],
        ["Cor pulmonale signs", "JVD, right ventricular heave, hepatomegaly, peripheral oedema",
         "Evidence of severe chronic respiratory impairment affecting right heart"],
        ["Clubbing", "Loss of nail-bed angle; increased sponginess; Schamroth sign",
         "Lung cancer, IPF, CF, bronchiectasis, mesothelioma; NOT COPD"],
        ["Central cyanosis", "Blue discolouration of lips/tongue",
         ">5 g/dL deoxygenated Hb; seen in hypoxaemic respiratory disorders"],
        ["Peripheral oedema\n(bilateral)", "Pitting oedema lower limbs",
         "Cor pulmonale; consider also DVT/PE if unilateral"],
        ["JVD", "Visible jugular venous pulsations at >3 cm above sternal angle sitting at 45°",
         "Volume overload; right heart failure; tension pneumothorax"],
        ["Hoover sign", "Inward motion of lower lateral rib cage on inspiration",
         "Emphysema — horizontal diaphragm pulls costal margin inward"],
        ["Tracheal tug", "Larynx descends on inspiration", "Severe airway obstruction; dynamic hyperinflation"],
    ]
    els.append(make_table(signs_data, [3.5*cm, 6.0*cm, 6.0*cm], header_bg=C_RED))

    els.append(Spacer(1, 8))

    # ── 7. AUSCULTATION ZONES ──────────────────────────────────────
    els.append(section_header("7  AUSCULTATION ZONES & TIPS"))
    els.append(Spacer(1, 4))

    zones_data = [
        ["Posterior zones (3 each side)", "Upper zone T2-T4 · Middle zone T4-T6 · Lower zone T6-T9"],
        ["Anterior zones", "Apices above clavicles · Upper chest · Lower chest down to 6th rib"],
        ["Right middle lobe / Lingula", "Heard ONLY anteriorly (right 4th-6th ICS, left 4th-6th ICS)"],
        ["Lateral zones", "Axillary lines — important for effusions and pneumothorax"],
    ]
    t_zones = Table(zones_data, colWidths=[5*cm, W - 5*cm])
    t_zones.setStyle(TableStyle([
        ("ROWBACKGROUNDS", (0,0), (-1,-1), [C_ACCENT, C_WHITE]),
        ("GRID",           (0,0), (-1,-1), 0.4, C_BORDER),
        ("FONTNAME",       (0,0), (0,-1),  "Helvetica-Bold"),
        ("FONTSIZE",       (0,0), (-1,-1), 8.5),
        ("TOPPADDING",     (0,0), (-1,-1), 4),
        ("BOTTOMPADDING",  (0,0), (-1,-1), 4),
        ("LEFTPADDING",    (0,0), (-1,-1), 6),
        ("VALIGN",         (0,0), (-1,-1), "MIDDLE"),
    ]))
    els.append(t_zones)

    els.append(Spacer(1, 6))
    tips = [
        ("Always compare", "auscultate one level on the right, then the same level on the left — before moving down."),
        ("Breath-sound quality", "a vesicular sound becoming bronchial at the periphery = consolidation until proven otherwise."),
        ("Crackle timing", "early inspiratory crackles = large-airway disease (COPD, bronchiectasis); late inspiratory fine crackles = alveolar/interstitial disease (oedema, IPF, pneumonia)."),
        ("Cleared by cough?", "rhonchi and some coarse crackles clear with coughing; wheeze and fine crackles do not."),
        ("Silent chest", "no wheeze despite severe asthma = no airflow — an emergency, not improvement."),
        ("Pleural rub timing", "present in both phases but disappears when significant effusion cushions the pleural surfaces."),
    ]
    els.append(mini_header("Examination Tips"))
    for heading, body in tips:
        els.append(Paragraph(
            f"<b>• {heading}:</b>  {body}", bullet_style))

    els.append(Spacer(1, 8))

    # ── 8. SYSTEMATIC APPROACH SUMMARY ────────────────────────────
    els.append(section_header("8  SYSTEMATIC APPROACH — BEDSIDE CHECKLIST"))
    els.append(Spacer(1, 4))

    steps = [
        ("1", "General", "Rate, SpO2, SpO2 on exertion, cyanosis, clubbing, JVD, oedema, use of accessory muscles"),
        ("2", "Inspect chest", "Shape, symmetry, movement, AP diameter, scars, deformities, retractions"),
        ("3", "Palpate", "Trachea, apex beat, chest expansion (hands on back), tactile fremitus ('99')"),
        ("4", "Percuss", "All zones posteriorly → anteriorly → laterally; map diaphragm; measure excursion"),
        ("5", "Auscultate", "All zones; normal sounds → quality → added sounds; vocal resonance if needed"),
        ("6", "Synthesise", "Integrate inspection + palpation + percussion + auscultation → pattern recognition"),
    ]
    step_tbl = Table(
        [[Paragraph(n, ParagraphStyle("step_n", fontName="Helvetica-Bold", fontSize=10,
                                       textColor=C_WHITE, alignment=TA_CENTER, leading=13)),
          Paragraph(h, ParagraphStyle("step_h", fontName="Helvetica-Bold", fontSize=9,
                                       textColor=C_DARK_BLUE, leading=12)),
          Paragraph(d, ParagraphStyle("step_d", fontName="Helvetica", fontSize=8.5,
                                       textColor=colors.HexColor("#1c2833"), leading=12))]
         for n, h, d in steps],
        colWidths=[1.1*cm, 2.8*cm, W - 3.9*cm],
        repeatRows=0
    )
    cmds = [
        ("VALIGN",       (0,0), (-1,-1), "MIDDLE"),
        ("TOPPADDING",   (0,0), (-1,-1), 5),
        ("BOTTOMPADDING",(0,0), (-1,-1), 5),
        ("LEFTPADDING",  (0,0), (-1,-1), 6),
        ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("GRID",         (0,0), (-1,-1), 0.4, C_BORDER),
        ("ROWBACKGROUNDS",(0,0), (-1,-1), [C_ACCENT, C_WHITE]),
    ]
    for i, (n, h, d) in enumerate(steps):
        cmds.append(("BACKGROUND", (0,i), (0,i), C_MID_BLUE))
    step_tbl.setStyle(TableStyle(cmds))
    els.append(step_tbl)

    els.append(Spacer(1, 10))

    # ── Footer ─────────────────────────────────────────────────────
    els.append(hr())
    els.append(Paragraph(
        "Sources: Harrison's Principles of Internal Medicine 22E (2025) · Murray &amp; Nadel's Textbook of Respiratory Medicine · "
        "Textbook of Family Medicine 9e · Goldman-Cecil Medicine · Orris Medical Reference",
        ParagraphStyle("footer", fontName="Helvetica-Oblique", fontSize=7, textColor=C_BORDER,
                       alignment=TA_CENTER)))

    return els

# ── BUILD ──────────────────────────────────────────────────────────────────────
all_elements = page1() + page2() + page3()
doc.build(all_elements)
print(f"PDF saved to: {OUTPUT}")
Running Command

cd /tmp/workspace/lung-exam-pdf && python build_pdf.py 2>&1

Running Command

ls -lh /tmp/workspace/lung-exam-pdf/Lung_Exam_Quick_Reference.pdf

Your PDF is ready. Here's what's included across the 3-page guide:

Lung Exam Quick Reference

PDF Document · PDF


Page 1 - Inspect · Palpate · Percuss
  • Inspection: General signs table (cyanosis, clubbing, tachypnea) + Chest wall findings (barrel chest, kyphoscoliosis, retractions) in a two-column layout
  • Breathing patterns table (Hoover sign, Kussmaul, accessory muscles, paradoxical motion)
  • Palpation table: trachea/apex shift directions, chest expansion, tactile fremitus changes
  • Percussion table: all 5 notes (resonant → stony dull → hyperresonant → tympanic) with causes
Page 2 - Auscultation + Master Comparison Table
  • Normal breath sounds (vesicular / bronchovesicular / bronchial) with I:E ratios
  • Added sounds table (fine/coarse crackles, wheeze, rhonchi, stridor, pleural rub) with key associations
  • Vocal resonance tests (bronchophony, whispered pectoriloquy, egophony)
  • Full 7-column comparison table: Consolidation · Pleural Effusion · Pneumothorax · Atelectasis · Asthma · COPD · IPF
Page 3 - Clinical Signs + Checklist
  • Important signs table (pulsus paradoxus, cor pulmonale, Hoover sign, tracheal tug, etc.)
  • Auscultation zones and tips (silent chest warning, crackle timing, pleural rub rule)
  • 6-step bedside checklist in a colour-coded sequential table
This is a shared conversation. Sign in to Orris to start your own chat.