less detail on low zooms

less detail on low zooms

/* paths (footway, cycleway, track) are dotted, the halo is behind the
   dots */
@path_halo_opacity: 0.3;
@path_halo_colour: white;
@path_dot_opacity: 1.0;

/* highway labels */
@highway_face_name: "Liberation Sans Regular";
@highway_text_fill: white;
@highway_halo_fill: black;
@highway_halo_radius: 2.0;

/*
  service      black
  residential  black
  unclassified black
  tertiary     yellow
  secondary    orange
  primary      red
  trunk        blue
  motorway     dark red
*/

#highway-labels[highway='footway'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='cycleway'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='path'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='track'][zoom > 13] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}

#highway-labels[highway='service'][zoom > 16] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='residential'][zoom > 15] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='unclassified'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='tertiary'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='secondary'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='primary'][zoom > 13] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='trunk'][zoom > 12] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='motorway'][zoom > 11] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}

/* default style for all path, footway, track, cycleways and roads */
.highway[zoom > 12] {
  line-color: #000000;
  line-opacity: 0.5;
  line-join: round;
  line-cap: round;

  line-width: 1;
}

/* defaults for roads with cars */
.road[zoom > 11] {
  ::oneway_arrow[oneway='yes'][zoom > 14] {
    marker-type: arrow;

    marker-width: 2;
    marker-line-width: 2;

    marker-opacity: 1.0;
    marker-line-opacity: 1.0;

    marker-fill: white;
    marker-line-color: white;

    marker-spacing: 100;
  }

  line-color: #000000;
  line-join: round;
  line-cap: round;

  line-width: 1;

  [zoom > 17] { line-width: 15; line-opacity: 0.5; }
  [zoom = 17] { line-width: 10; line-opacity: 0.5; }
  [zoom = 16] { line-width: 5; line-opacity: 0.5; }
  [zoom = 15] { line-width: 2.25; line-opacity: 0.6; }
  [zoom = 14] { line-width: 1.5; line-opacity: 0.7; }
  [zoom = 13] { line-width: 1; line-opacity: 0.8; }
  [zoom = 12] { line-width: 0.5; line-opacity: 0.8; }

  ::bridge[bridge='yes'] {
    /*
    ideally we would draw two lines outside the existing line,
    but the current compositing method in mapnik won't allow this
    so easily with transparency

    line-color: #000000;
    line-opacity: 0.5;
    line-join: round;
    line-cap: round;

    [zoom > 17] { line-width: 20; }
    [zoom = 17] { line-width: 15; }
    [zoom = 16] { line-width: 7; }
    [zoom = 15] { line-width: 3; }
    [zoom = 14] { line-width: 2; }
    [zoom = 13] { line-width: 1.5; }
    [zoom = 12] { line-width: 0.75; }
    */
  }

}

.highway#footway[zoom > 13] {
  ::halo{
    line-color: @path_halo_colour;
    line-opacity: @path_halo_opacity;

    [zoom > 18] { line-width: 9 }
    [zoom = 18] { line-width: 6 }
    [zoom = 17] { line-width: 4 }
    [zoom < 17] { line-width: 2 }
  }

  line-color: #FF0000;
  line-opacity: @path_dot_opacity;

  [zoom > 18] { line-width: 8  ; line-dasharray: 2,16 }
  [zoom = 18] { line-width: 5  ; line-dasharray: 2,8 }
  [zoom = 17] { line-width: 3  ; line-dasharray: 1,4 }
  [zoom = 16] { line-width: 2  ; line-dasharray: 1,3 }
  [zoom < 16] { line-width: 1.5; line-dasharray: 1,2 }

  [highway='steps']{
    line-cap: butt;

    [zoom > 18] { line-dasharray: 3,4 }
    [zoom = 18] { line-dasharray: 2,3 }
    [zoom < 18] { line-dasharray: 1,2 }
  }

}

.highway#path[zoom > 12] {
  ::halo{
    line-color: @path_halo_colour;
    line-opacity: @path_halo_opacity;

    [zoom > 18] { line-width: 9 }
    [zoom = 18] { line-width: 6 }
    [zoom = 17] { line-width: 4 }
    [zoom < 17] { line-width: 2 }
  }
  line-color: #000000;
  line-opacity: @path_dot_opacity;

  [zoom > 18] { line-width: 8  ; line-dasharray: 2,16 }
  [zoom = 18] { line-width: 5  ; line-dasharray: 2,8 }
  [zoom = 17] { line-width: 3  ; line-dasharray: 1,4 }
  [zoom = 16] { line-width: 2  ; line-dasharray: 1,3 }
  [zoom < 16] { line-width: 1.5; line-dasharray: 1,2 }

}

.highway#track[zoom > 12] {
  ::halo{
    line-color: @path_halo_colour;
    line-opacity: @path_halo_opacity;

    [zoom > 18] { line-width: 16 }
    [zoom = 18] { line-width: 10 }
    [zoom = 17] { line-width: 6 }
    [zoom = 16] { line-width: 4.5 }
    [zoom = 15] { line-width: 3.2 }
    [zoom = 14] { line-width: 3 }
    [zoom = 13] { line-width: 2.4 }
  }

  line-color: #6d4f15;
  line-opacity: @path_dot_opacity;

  [zoom > 18] { line-width: 8  ; line-dasharray: 2,16 }
  [zoom = 18] { line-width: 5  ; line-dasharray: 3,4 }
  [zoom = 17] { line-width: 3  ; line-dasharray: 3,4 }
  [zoom = 16] { line-width: 2.1; line-dasharray: 3,4 }
  [zoom = 15] { line-width: 1.8; line-dasharray: 3,4 }
  [zoom = 14] { line-width: 1.5; line-dasharray: 3,4 }
  [zoom = 13] { line-width: 1.2  ; line-dasharray: 3,4 }

}

.highway#cycleway[zoom > 12] {
  ::halo{
    line-color: @path_halo_colour;
    line-opacity: @path_halo_opacity;

    [zoom > 18] { line-width: 9 }
    [zoom = 18] { line-width: 6 }
    [zoom = 17] { line-width: 4 }
    [zoom < 17] { line-width: 2 }
  }

  line-color: #0000ff;
  line-opacity: @path_dot_opacity;

  [zoom > 18] { line-width: 8  ; line-dasharray: 2,16 }
  [zoom = 18] { line-width: 5  ; line-dasharray: 2,8 }
  [zoom = 17] { line-width: 3  ; line-dasharray: 1,4 }
  [zoom = 16] { line-width: 2  ; line-dasharray: 1,3 }
  [zoom < 16] { line-width: 1.5; line-dasharray: 1,2 }

}

.road#service[zoom > 11] {
  [zoom > 17] { line-width: 7.5; }
  [zoom = 17] { line-width: 5; }
  [zoom = 16] { line-width: 2.5; }
  [zoom = 15] { line-width: 1; }
  [zoom = 14] { line-width: 0.75; }
  [zoom = 13] { line-width: 0.5; }
  [zoom = 12] { line-width: 0.25; }

  [service='parking_aisle'][zoom > 14] {
    [zoom > 17] { line-width: 3; }
    [zoom = 17] { line-width: 2.5; }
    [zoom = 16] { line-width: 1.25; }
    [zoom = 15] { line-width: 0.25; }
  }
}

.road#residential[zoom > 11] {
  ::living_street[highway='living_street'][zoom > 14] {
    line-cap: butt;
    line-dasharray: 6,6;
    line-color: #76d276;
    line-opacity: 0.5;

    [zoom > 17] { line-width: 10; }
    [zoom = 17] { line-width: 7.5; }
    [zoom = 16] { line-width: 3.5; }
    [zoom = 15] { line-width: 1.75; }
    [zoom = 14] { line-width: 1; }
    [zoom = 13] { line-width: 0.75; }
    [zoom = 12] { line-width: 0.3; }
  }

  [zoom > 17] { line-width: 10; }
  [zoom = 17] { line-width: 7.5; }
  [zoom = 16] { line-width: 3.5; }
  [zoom = 15] { line-width: 1.75; }
  [zoom = 14] { line-width: 1; }
  [zoom = 13] { line-width: 0.75; }
  [zoom = 12] { line-width: 0.3; }

}

/* sets the colours for the road classification,
  but also draws the default line where .road doesn't apply
  ie. for zoom < 12 */
.road#unclassified{
}

.road#tertiary[zoom > 11]{
  line-color: #faf555;
}

.road#secondary{
  line-color: #d46833;
  [zoom = 12] { line-width: 1.0}
}

.road#primary{
  line-color: #cc3737;
  [zoom = 12] { line-width: 1.5}
}

.road#trunk{
  line-color: #243a7a;
  [zoom = 12] { line-width: 1.2}
}

.road#motorway{
  line-color: #7a2424;
  [zoom = 12] { line-width: 2.0}
}

/* paths (footway, cycleway, track) are dotted, the halo is behind the
   dots */
@path_halo_opacity: 0.3;
@path_halo_colour: white;
@path_dot_opacity: 1.0;

/* highway labels */
@highway_face_name: "Liberation Sans Regular";
@highway_text_fill: white;
@highway_halo_fill: black;
@highway_halo_radius: 2.0;

#highway-labels[highway='footway'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='cycleway'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='path'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='track'][zoom > 13] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}

#highway-labels[highway='service'][zoom > 16] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='residential'][zoom > 15] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='unclassified'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='tertiary'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='secondary'][zoom > 14] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='primary'][zoom > 13] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='trunk'][zoom > 12] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}
#highway-labels[highway='motorway'][zoom > 11] {
  text-name: "[name]";
  text-face-name: @highway_face_name;
  text-fill: @highway_text_fill;
  text-placement: line;
  text-halo-fill: @highway_halo_fill;
  text-halo-radius: @highway_halo_radius;

  [zoom > 21] { text-size: 30; }
  [zoom = 21] { text-size: 25; }
  [zoom = 20] { text-size: 20; }
  [zoom = 19] { text-size: 15; }
  [zoom = 18] { text-size: 13; }
  [zoom = 17] { text-size: 12; }
  [zoom = 16] { text-size: 11; }
  [zoom = 15] { text-size: 11; }
  [zoom = 14] { text-size: 10; }
  [zoom = 13] { text-size: 8; }
  [zoom = 12] {text-size: 8; }
}

/* default style for all path, footway, track, cycleways and roads */
.highway {
  line-color: #000000;
  line-opacity: 0.5;
  line-join: round;
  line-cap: round;

  line-width: 1;
}

/* defaults for roads with cars */
.road[zoom > 11] {
  ::oneway_arrow[oneway='yes'][zoom > 14] {
    marker-type: arrow;

    marker-width: 2;
    marker-line-width: 2;

    marker-opacity: 1.0;
    marker-line-opacity: 1.0;

    marker-fill: white;
    marker-line-color: white;

    marker-spacing: 100;
  }

  line-color: #000000;
  line-join: round;
  line-cap: round;

  line-width: 1;

  [zoom > 17] { line-width: 15; line-opacity: 0.5; }
  [zoom = 17] { line-width: 10; line-opacity: 0.5; }
  [zoom = 16] { line-width: 5; line-opacity: 0.5; }
  [zoom = 15] { line-width: 2.25; line-opacity: 0.6; }
  [zoom = 14] { line-width: 1.5; line-opacity: 0.7; }
  [zoom = 13] { line-width: 1; line-opacity: 0.8; }
  [zoom = 12] { line-width: 0.5; line-opacity: 0.8; }

  ::bridge[bridge='yes'] {
    /*
    ideally we would draw two lines outside the existing line,
    but the current compositing method in mapnik won't allow this
    so easily with transparency

    line-color: #000000;
    line-opacity: 0.5;
    line-join: round;
    line-cap: round;

    [zoom > 17] { line-width: 20; }
    [zoom = 17] { line-width: 15; }
    [zoom = 16] { line-width: 7; }
    [zoom = 15] { line-width: 3; }
    [zoom = 14] { line-width: 2; }
    [zoom = 13] { line-width: 1.5; }
    [zoom = 12] { line-width: 0.75; }
    */
  }

}

.highway#footway[zoom > 13] {
  ::halo{
    line-color: @path_halo_colour;
    line-opacity: @path_halo_opacity;

    [zoom > 18] { line-width: 9 }
    [zoom = 18] { line-width: 6 }
    [zoom = 17] { line-width: 4 }
    [zoom < 17] { line-width: 2 }
  }

  line-color: #FF0000;
  line-opacity: @path_dot_opacity;

  [zoom > 18] { line-width: 8  ; line-dasharray: 2,16 }
  [zoom = 18] { line-width: 5  ; line-dasharray: 2,8 }
  [zoom = 17] { line-width: 3  ; line-dasharray: 1,4 }
  [zoom = 16] { line-width: 2  ; line-dasharray: 1,3 }
  [zoom < 16] { line-width: 1.5; line-dasharray: 1,2 }

  [highway='steps']{
    line-cap: butt;

    [zoom > 18] { line-dasharray: 3,4 }
    [zoom = 18] { line-dasharray: 2,3 }
    [zoom < 18] { line-dasharray: 1,2 }
  }

}

.highway#path[zoom > 12] {
  ::halo{
    line-color: @path_halo_colour;
    line-opacity: @path_halo_opacity;

    [zoom > 18] { line-width: 9 }
    [zoom = 18] { line-width: 6 }
    [zoom = 17] { line-width: 4 }
    [zoom < 17] { line-width: 2 }
  }
  line-color: #000000;
  line-opacity: @path_dot_opacity;

  [zoom > 18] { line-width: 8  ; line-dasharray: 2,16 }
  [zoom = 18] { line-width: 5  ; line-dasharray: 2,8 }
  [zoom = 17] { line-width: 3  ; line-dasharray: 1,4 }
  [zoom = 16] { line-width: 2  ; line-dasharray: 1,3 }
  [zoom < 16] { line-width: 1.5; line-dasharray: 1,2 }

}

.highway#track[zoom > 12] {
  ::halo{
    line-color: @path_halo_colour;
    line-opacity: @path_halo_opacity;

    [zoom > 18] { line-width: 16 }
    [zoom = 18] { line-width: 10 }
    [zoom = 17] { line-width: 6 }
    [zoom = 16] { line-width: 4.5 }
    [zoom = 15] { line-width: 3.2 }
    [zoom = 14] { line-width: 3 }
    [zoom = 13] { line-width: 2.4 }
  }

  line-color: #6d4f15;
  line-opacity: @path_dot_opacity;

  [zoom > 18] { line-width: 8  ; line-dasharray: 2,16 }
  [zoom = 18] { line-width: 5  ; line-dasharray: 3,4 }
  [zoom = 17] { line-width: 3  ; line-dasharray: 3,4 }
  [zoom = 16] { line-width: 2.1; line-dasharray: 3,4 }
  [zoom = 15] { line-width: 1.8; line-dasharray: 3,4 }
  [zoom = 14] { line-width: 1.5; line-dasharray: 3,4 }
  [zoom = 13] { line-width: 1.2  ; line-dasharray: 3,4 }

}

.highway#cycleway[zoom > 12] {
  ::halo{
    line-color: @path_halo_colour;
    line-opacity: @path_halo_opacity;

    [zoom > 18] { line-width: 9 }
    [zoom = 18] { line-width: 6 }
    [zoom = 17] { line-width: 4 }
    [zoom < 17] { line-width: 2 }
  }

  line-color: #0000ff;
  line-opacity: @path_dot_opacity;

  [zoom > 18] { line-width: 8  ; line-dasharray: 2,16 }
  [zoom = 18] { line-width: 5  ; line-dasharray: 2,8 }
  [zoom = 17] { line-width: 3  ; line-dasharray: 1,4 }
  [zoom = 16] { line-width: 2  ; line-dasharray: 1,3 }
  [zoom < 16] { line-width: 1.5; line-dasharray: 1,2 }

}

.road#service[zoom > 11] {
  [zoom > 17] { line-width: 7.5; }
  [zoom = 17] { line-width: 5; }
  [zoom = 16] { line-width: 2.5; }
  [zoom = 15] { line-width: 1; }
  [zoom = 14] { line-width: 0.75; }
  [zoom = 13] { line-width: 0.5; }
  [zoom = 12] { line-width: 0.25; }

  [service='parking_aisle'][zoom > 14] {
    [zoom > 17] { line-width: 3; }
    [zoom = 17] { line-width: 2.5; }
    [zoom = 16] { line-width: 1.25; }
    [zoom = 15] { line-width: 0.25; }
  }
}

.road#residential[zoom > 11] {
  ::living_street[highway='living_street'][zoom > 14] {
    line-cap: butt;
    line-dasharray: 6,6;
    line-color: #76d276;
    line-opacity: 0.5;

    [zoom > 17] { line-width: 10; }
    [zoom = 17] { line-width: 7.5; }
    [zoom = 16] { line-width: 3.5; }
    [zoom = 15] { line-width: 1.75; }
    [zoom = 14] { line-width: 1; }
    [zoom = 13] { line-width: 0.75; }
    [zoom = 12] { line-width: 0.3; }
  }

  [zoom > 17] { line-width: 10; }
  [zoom = 17] { line-width: 7.5; }
  [zoom = 16] { line-width: 3.5; }
  [zoom = 15] { line-width: 1.75; }
  [zoom = 14] { line-width: 1; }
  [zoom = 13] { line-width: 0.75; }
  [zoom = 12] { line-width: 0.3; }

}

/* sets the colours for the road classification,
  but also draws the default line where .road doesn't apply
  ie. for zoom < 12 */
.road#unclassified{
}

.road#tertiary{
  line-color: #faf555;
}

.road#secondary{
  line-color: #d46833;
  [zoom = 12] { line-width: 1.0}
}

.road#primary{
  line-color: #cc3737;
  [zoom = 12] { line-width: 1.5}
}

.road#trunk{
  line-color: #243a7a;
  [zoom = 12] { line-width: 1.2}
}

.road#motorway{
  line-color: #7a2424;
  [zoom = 12] { line-width: 2.0}
}


poi work

poi work

// =====================================================================
// POI LABELS
// =====================================================================

#poi_label[type!='Aerodrome'][type!='Rail Station'][scalerank<4] {
  [zoom>=8][zoom<14][localrank=1],
  [zoom>=14][scalerank=1][localrank=1],
  [zoom>=15][scalerank<=2][localrank=1],
  [zoom>=16][scalerank<=3][localrank=1],
  [zoom>=17][localrank<=4],
  [zoom>=18][localrank<=16],
  [zoom>=19] {
    text-name: @name;
    text-face-name: @sans;
    text-fill: #555;
    text-halo-fill: fadeout(#fff,75);
    text-halo-radius: 1;
    text-halo-rasterizer: fast;
    text-line-spacing: -4;
    text-wrap-width: 80;
    text-wrap-before: true;
    text-min-distance: 8;
    [scalerank=1] {
      [zoom>=15] { text-size: 11; text-wrap-width: 100; }
      [zoom>=16] { text-size: 12; text-wrap-width: 120; }
      [zoom>=17] { text-size: 14; text-wrap-width: 130; }
    }
    [scalerank=2] {
      [zoom>=16] { text-size: 11; text-wrap-width: 100; }
      [zoom>=17] { text-size: 12; text-wrap-width: 120; }
    }
    [scalerank>=3] {
      [zoom>=17] { text-size: 11; text-wrap-width: 100; }
      [zoom>=19] { text-size: 12; text-wrap-width: 120; }
    }
  }
}

// =====================================================================
// 6__ TRANSPORTATION ICONS + LABELS
// =====================================================================

// 6_1__ Rail Stations _________________________________________________

// Note: != null condition was a workaround for mapnik#1952 but is a good idea in general
#poi_label[type='Rail Station'][network!=null][scalerank=1][zoom>=14],
#poi_label[type='Rail Station'][network!=null][scalerank=2][zoom>=15],
#poi_label[type='Rail Station'][network!=null][scalerank=3][zoom>=16],
#poi_label[type='Rail Station'][network!=null][scalerank=4][zoom>=17]{
  marker-file: url("img/rail/[network]-12.svg");
  marker-height: 12;
  marker-allow-overlap: false;
  [zoom=16] {
    marker-file: url("img/rail/[network]-18.svg");
    marker-height: 18;
  }
  [zoom>16] {
    marker-file: url("img/rail/[network]-12.svg");
    marker-height: 24;
    [scalerank=4] { marker-height: 12; }
    [scalerank=4][zoom>18] {
      marker-file: url("img/rail/[network]-18.svg");
      marker-height: 18;
    }
  }
  [zoom>15][scalerank!=4],
  [scalerank=4][zoom>=18] {
    text-name: @name;
    text-face-name: @sans;
    text-fill: @transport_text;
    text-halo-fill: #fff;
    text-halo-radius: 1.5;
    text-halo-rasterizer: fast;
    text-size: 11;
    text-wrap-width: 80;
    text-placement-type: simple;
    text-dy: 11;
    [zoom>=17][scalerank!=4] {
      text-size: 12;
      text-halo-radius: 2;
      text-dy: 15;
    }
  }
}

// 6_2__ Airports ______________________________________________________

#poi_label[type='Aerodrome'][zoom>=10] {
  marker-file: url("img/maki-classic/[maki]-12.svg");
  marker-transform: translate(0,-6);
  text-name: "''";
  text-size: 10;
  text-fill: @transport_text;
  text-halo-fill: #fff;
  text-halo-radius: 1;
  text-halo-rasterizer: fast;
  text-face-name: @sans;
  text-dy: 1;
  [zoom>=11][zoom<=13][scalerank=1],
  [zoom>=12][zoom<=13][scalerank=2] {
    text-name: [ref];
  }
  [zoom>=14] {
    text-name: @name;
    text-wrap-before: true;
  }
  [zoom>=11][scalerank=1],
  [zoom>=12][scalerank=2],
  [zoom>=14] {
    marker-file: url("img/maki-classic/[maki]-18.svg");
    marker-transform: translate(0,-9);
    text-size: 10;
    text-wrap-width: 80;
  }
  [zoom>=13][scalerank=1],
  [zoom>=14][scalerank=2],
  [zoom>=15] {
    marker-file: url("img/maki-classic/[maki]-24.svg");
    marker-transform: translate(0,-12);
    text-size: 12;
    text-wrap-width: 120;
  }
  [zoom>=14][scalerank=1],
  [zoom>=15][scalerank=2],
  [zoom>=16] {
    text-size: 14;
    text-wrap-width: 160;
  }
}

// =====================================================================
// POI LABELS
// =====================================================================

#poi_label[type!='Aerodrome'][type!='Rail Station'][scalerank<4] {
  [zoom<14],
  [zoom>=14][scalerank=1][localrank<=1],
  [zoom>=15][scalerank<=2][localrank<=1],
  [zoom>=16][scalerank<=3][localrank<=1],
  [zoom>=17][localrank<=4],
  [zoom>=18][localrank<=16],
  [zoom>=19] {
    text-name: @name;
    text-face-name: @sans;
    text-fill: #555;
    text-halo-fill: fadeout(#fff,75);
    text-halo-radius: 1;
    text-halo-rasterizer: fast;
    text-line-spacing: -4;
    text-wrap-width: 80;
    text-wrap-before: true;
    [scalerank=1] {
      [zoom>=15] { text-size: 11; text-wrap-width: 100; }
      [zoom>=16] { text-size: 12; text-wrap-width: 120; }
      [zoom>=17] { text-size: 14; text-wrap-width: 130; }
    }
    [scalerank=2] {
      [zoom>=16] { text-size: 11; text-wrap-width: 100; }
      [zoom>=17] { text-size: 12; text-wrap-width: 120; }
    }
    [scalerank>=3] {
      [zoom>=17] { text-size: 11; text-wrap-width: 100; }
      [zoom>=19] { text-size: 12; text-wrap-width: 120; }
    }
  }
}

// =====================================================================
// 6__ TRANSPORTATION ICONS + LABELS
// =====================================================================

// 6_1__ Rail Stations _________________________________________________

// Note: != null condition was a workaround for mapnik#1952 but is a good idea in general
#poi_label[type='Rail Station'][network!=null][scalerank=1][zoom>=14],
#poi_label[type='Rail Station'][network!=null][scalerank=2][zoom>=15],
#poi_label[type='Rail Station'][network!=null][scalerank=3][zoom>=16],
#poi_label[type='Rail Station'][network!=null][scalerank=4][zoom>=17]{
  marker-file: url("img/rail/[network]-12.svg");
  marker-height: 12;
  marker-allow-overlap: false;
  [zoom=16] {
    marker-file: url("img/rail/[network]-18.svg");
    marker-height: 18;
  }
  [zoom>16] {
    marker-file: url("img/rail/[network]-12.svg");
    marker-height: 24;
    [scalerank=4] { marker-height: 12; }
    [scalerank=4][zoom>18] {
      marker-file: url("img/rail/[network]-18.svg");
      marker-height: 18;
    }
  }
  [zoom>15][scalerank!=4],
  [scalerank=4][zoom>=18] {
    text-name: @name;
    text-face-name: @sans;
    text-fill: @transport_text;
    text-halo-fill: #fff;
    text-halo-radius: 1.5;
    text-halo-rasterizer: fast;
    text-size: 11;
    text-wrap-width: 80;
    text-placement-type: simple;
    text-dy: 11;
    [zoom>=17][scalerank!=4] {
      text-size: 12;
      text-halo-radius: 2;
      text-dy: 15;
    }
  }
}

// 6_2__ Airports ______________________________________________________

#poi_label[type='Aerodrome'][zoom>=10] {
  marker-file: url("img/maki-classic/[maki]-12.svg");
  text-name: "''";
  text-size: 10;
  text-fill: @transport_text;
  text-halo-fill: #fff;
  text-halo-radius: 1;
  text-halo-rasterizer: fast;
  text-face-name: @sans;
  text-placement-type: simple;
  text-placements: "S,N,E,W";
  text-dx: 8; text-dy: 8;
  [zoom>=11][zoom<=13][scalerank=1],
  [zoom>=12][zoom<=13][scalerank=2] {
    text-name: [ref];
  }
  [zoom>=14] {
    text-name: @name;
    text-wrap-before: true;
  }
  [zoom>=11][scalerank=1],
  [zoom>=12][scalerank=2],
  [zoom>=14] {
    marker-file: url("img/maki-classic/[maki]-18.svg");
    text-size: 10;
    text-dx: 12; text-dy: 12;
    text-wrap-width: 80;
  }
  [zoom>=13][scalerank=1],
  [zoom>=14][scalerank=2],
  [zoom>=15] {
    marker-file: url("img/maki-classic/[maki]-24.svg");
    text-size: 12;
    text-dx: 15; text-dy: 15;
    text-wrap-width: 100;
  }
  [zoom>=14][scalerank=1],
  [zoom>=15][scalerank=2],
  [zoom>=16] {
    marker-file: url("img/maki-classic/[maki]-24.svg");
    text-size: 14;
    text-dx: 19; text-dy: 19;
    text-wrap-width: 120;
  }
}


Don't put an <anchoredText> in <tempo>

Don't put an <anchoredText> in <tempo>

function InitTextHandlers() {
    // QUESTION: We could also take an argument and throw all text handlers from
    // extensions into the same dictionary

    Self._property:TextHandlers = CreateDictionary(
        'text.staff.expression', 'ExpressionTextHandler',
        'text.system.page_aligned.title', 'PageTitleHandler',
        'text.system.page_aligned.subtitle', 'PageTitleHandler',
        'text.system.page_aligned.composer', 'PageComposerTextHandler',
        'text.system.tempo', 'TempoTextHandler',
        'text.staff.space.figuredbass', 'FiguredBassTextHandler',
        'text.staff.plain', 'CreateAnchoredText'
    );

    Self._property:TextSubstituteMap = CreateDictionary(
        'Title', CreateSparseArray('Title'),
        'Subtitle', CreateSparseArray('Title', CreateDictionary('type', 'subordinate')),
        'Dedication', CreateSparseArray('Dedication'),
        // <composer>, <arranger>, <lyricist>, <userRestrict> and <publisher>
        // are only allowed in a few places, e.g. metadata or title pages.
        // We therfore use more generic elements
        'Composer', CreateSparseArray('PersName', CreateDictionary('role', 'Composer')),
        'Arranger', CreateSparseArray('PersName', CreateDictionary('role', 'Arranger')),
        'Lyricist', CreateSparseArray('PersName', CreateDictionary('role', 'Lyricist')),
        'Artist', CreateSparseArray('PersName', CreateDictionary('role', 'Artist')),
        // <useRestrict> is only allowed on <titlePage>, so use generic element
        'Copyright', CreateSparseArray('Seg', CreateDictionary('type', 'Copyright')),
        // <publisher> is only allowed in a few places, so use generic element
        // We don't even know if it's a person or an institution
        'Publisher', CreateSparseArray('Seg', CreateDictionary('type', 'Publisher')),
        'MoreInfo', CreateSparseArray('Seg', CreateDictionary('type', 'MoreInfo')),
        'PartName', CreateSparseArray('Seg', CreateDictionary('type', 'PartName'))
    );
}  //$end

function HandleText (textObject) {
    // TODO: Move InitTextHandlers() call to a global initialization function
    InitTextHandlers();
    if (null != Self._property:Extension and null != Extension.TextHandlers)
    {
        // TODO: We need to check for both StyleId *and* StyleAsText so we can
        // handle custom styles
        textHandler = Extension.TextHandlers[textObject.StyleId];
        if (null != textHandler)
        {
            return Extension.@textHandler(textObject);
        }
    }

    textHandler = TextHandlers[textObject.StyleId];
    if (null != textHandler)
    {
        return @textHandler(textObject);
    }
}  //$end

function ExpressionTextHandler (textObject) {
    dynam = AddBarObjectInfoToElement(textObject, libmei.Dynam());
    AddFormattedText(dynam, textObject);
    return dynam;
}  //$end

function PageTitleHandler (textObject) {
    anchoredText = libmei.AnchoredText();
    title = libmei.Title();
    if (textObject.StyleId = 'text.system.page_aligned.subtitle')
    {
        libmei.AddAttribute(title, 'type', 'subordinate');
    }

    libmei.AddChild(anchoredText, title);
    AddFormattedText(title, textObject);

    return anchoredText;
}  //$end

function PageComposerTextHandler (textObject) {
    // 'text.system.page_aligned.composer'
    return AddBarObjectInfoToElement(textObject, CreateAnchoredText(textObject));
}  //$end

function TempoTextHandler (textObject) {
    // 'text.system.tempo'
    tempo = libmei.Tempo();
    AddFormattedText(tempo, textObject);
    return tempo;
}  //$end

function FiguredBassTextHandler (textObject) {
    // 'text.staff.space.figuredbass'
    harm = AddBarObjectInfoToElement(textObject, libmei.Harm());
    fb = libmei.Fb();
    libmei.AddChild(harm, fb);
    ConvertFbFigures(fb, textObject);
    return harm;
}  //$end

function CreateAnchoredText (textObj) {
    //$module(ExportConverters.mss)
    anchoredText = libmei.AnchoredText();
    AddFormattedText(anchoredText, textObj);
    return anchoredText;
}  //$end

function AddFormattedText (parentElement, textObj) {
    textWithFormatting = textObj.TextWithFormatting;
    if (textWithFormatting.NumChildren < 2 and CharAt(textWithFormatting[0], 0) != '\\')
    {
        libmei.SetText(parentElement, textObj.Text);
        return null;
    }

    nodes = CreateSparseArray();

    state = CreateDictionary(
        'currentText', null,
        'rendAttributes', CreateDictionary(),
        'rendFlags', CreateDictionary(),
        // TODO: Also track the active character style (mainly
        // `\ctext.character.musictext\`, and custom styles)
        'nodes', nodes,
        'paragraphs', null
    );

    for each component in textObj.TextWithFormatting
    {
        switch (Substring(component, 0, 2))
        {
            case ('\\n')
            {
                PushStyledText(state);
                nodes.Push(libmei.Lb());
            }
            case ('\\N')
            {
                PushStyledText(state);
                // TODO: Add <p> if it is allowed within parentElement (use libmei.GetName())
                nodes.Push(libmei.Lb());
            }
            case ('\\B')
            {
                SwitchTextStyle(state, 'fontweight', 'bold');
            }
            case ('\\b')
            {
                SwitchTextStyle(state, 'fontweight', 'normal');
            }
            case ('\\I')
            {
                SwitchTextStyle(state, 'fontstyle', 'italic');
            }
            case ('\\i')
            {
                SwitchTextStyle(state, 'fontstyle', 'normal');
            }
            case ('\\U')
            {
                SwitchTextStyle(state, 'rend', 'underline', true);
            }
            case ('\\u')
            {
                SwitchTextStyle(state, 'rend', 'underline', false);
            }
            case ('\\f')
            {
                SwitchFont(state, GetTextCommandArg(component));
            }
            case ('\\c')
            {
                // TODO: Can we sensibly handle a character style change? The
                // only built-in one seem to be `text.character.musictext`. We
                // might want to allow Extensions to handle custom character
                // styles.
                ;
            }
            case ('\\s')
            {
                fontsize = ConvertOffsetsToMEI(GetTextCommandArg(component));
                SwitchTextStyle(state, 'fontsize', fontsize);
            }
            case ('\\v')
            {
                // Vertical scale change (vertical stretching). Probably not
                // possible to handle
                ;
            }
            case ('\\h')
            {
                // Horizontal scale change (horizontal stretching). Probably not
                // possible to handle
                ;
            }
            case ('\\t')
            {
                // Tracking. Probably not possible to handle.
                ;
            }
            case ('\\p')
            {
                SwitchBaselineAdjust(state, GetTextCommandArg(component));
            }
            case ('\\$') {
                AppendTextSubstitute(state, GetTextCommandArg(component));
            }
            case ('\\\\')
            {
                // According to the documentation, 'backslashes themselves are
                // represented by \\ , to avoid conflicting with the above
                // commands'. Though that does not seem to work, let's just
                // assume it does in case Avid fixes this.

                // We strip one leading backspace.
                state.currentText = state.currentText & Substring(component, 1);
            }
            default
            {
                // This is regular text
                state.currentText = state.currentText & component;
            }
        }
    }

    PushStyledText(state);

    nodeCount = nodes.Length;
    precedingElement = null;

    nodeIndex = 0;
    while (nodeIndex < nodeCount)
    {
        node = nodes[nodeIndex];
        if (IsObject(node))
        {
            // We have an element
            libmei.AddChild(parentElement, node);
            precedingElement = node;
        }
        else
        {
            // We have a text node
            text = node;
            // If there are multiple adjacent text nodes, we need to join them
            while (nodeIndex < nodeCount and not IsObject(nodes[nodeIndex + 1])) {
                nodeIndex = nodeIndex + 1;
                text = text & nodes[nodeIndex];
            }

            if (precedingElement = null)
            {
                libmei.SetText(parentElement, text);
            }
            else
            {
                libmei.SetTail(precedingElement, text);
            }
        }
        nodeIndex = nodeIndex + 1;
    }
}  //$end

function NewTextParagraph (state) {
    // TODO!
    ;
}  //$end

function GetTextCommandArg (command) {
    // Remove leading part, e.g. the '\$' or '\s' and trailing '\'
    return Substring(command, 2, Length(command) - 3);
}  //$end

function SwitchBaselineAdjust (state, param) {
    sup = (param = 'superscript');
    sub = (param = 'subscript');
    if (sup != state.rendFlags['sup'] or sub != state.rendFlags['sub']) {
        // Style changed, push the previous text before changing the style
        PushStyledText(state);
    }
    state.rendFlags['sup'] = sup;
    state.rendFlags['sub'] = sub;
}  //$end

function ResetTextStyles (state, infoOnly) {
    // If `infoOnly` is `true`, does not make any changes, only tells us if
    // there are re-settable styles
    if (null != state.rendAttributes)
    {
        for each Name attName in state.rendAttributes
        {
            if (infoOnly and state.rendAttributes[attName] != null)
            {
                return true;
            }
            state.rendAttributes[attName] = null;
        }
    }

    if (null != state.rendFlags)
    {
        for each Name flagName in state.rendFlags
        {
            if (infoOnly and state.rendFlags[flagName])
            {
                return true;
            }
            state.rendFlags[flagName] = false;
        }
    }
    return false;
}  //$end

function SwitchFont (state, fontName) {
    if (fontName = '_')
    {
        // Before resetting the style, we have to add text preceding the '\f_\'
        // style reset – but only if the style reset actually changes something.
        if (ResetTextStyles(state, true))
        {
            PushStyledText(state);
            ResetTextStyles(state, false);
        }
    }
    else
    {
        SwitchTextStyle(state, 'fontfam', fontName);
    }
}  //$end

function SwitchTextStyle (state, attName, value) {
    if (state.rendAttributes[attName] != value)
    {
        // Style changes, so append current text before modifying style state
        PushStyledText(state);
    }
    state.rendAttributes[attName] = value;
}  //$end

function SwitchRendFlags (state, flagName, value) {
    if (state.rendFlags[flagName] != value)
    {
        PushStyledText(state);
    }
    state.rendFlags[flagName] = value;
}  //$end

function PushStyledText (state) {
    if (state.currentText = '')
    {
        return null;
    }

    styleAttributes = GetStyleAttributes(state);
    if (null = styleAttributes)
    {
        // We attach unstyled text without wrapping it in <rend>
        state.nodes.Push(state.currentText);
    }
    else
    {
        rend = libmei.Rend();
        for each Name attName in styleAttributes {
            libmei.AddAttribute(rend, attName, styleAttributes[attName]);
        }
        libmei.SetText(rend, state.currentText);
        state.nodes.Push(rend);
    }

    state.currentText = '';
}  //$end

function GetStyleAttributes (state) {
    rendAttributes = null;

    if (null != state.rendAttributes)
    {
        for each Name attName in state.rendAttributes
        {
            value = state.rendAttributes[attName];
            if (null != value)
            {
                if (null = rendAttributes) {
                    rendAttributes = CreateDictionary();
                }
                rendAttributes[attName] = value;
            }
        }
    }

    if (null != state.rendFlags)
    {
        rendAttValue = '';
        firstRendFlag = true;
        for each Name flagName in state.rendFlags
        {
            flagActive = state.rendFlags[flagName];
            if (flagActive)
            {
                if (firstRendFlag = true)
                {
                    rendAttValue = rendAttValue & flagName;
                    firstRendFlag = false;
                }
                else
                {
                    rendAttValue = rendAttValue & ' ' & flagName;
                }
            }
        }
        if (rendAttValue != '')
        {
            if (null = rendAttributes) {
                rendAttributes = CreateDictionary();
            }
            rendAttributes['rend'] = rendAttValue;
        }
    }

    return rendAttributes;
}  //$end

function AppendTextSubstitute (state, substituteName) {
    score = Self._property:ActiveScore;

    textSubstituteInfo = TextSubstituteMap[substituteName];
    if (null = textSubstituteInfo)
    {
        // No known substitution. Sibelius renders those literally.
        state.currentText = state.currentText & '\\$' & substituteName & '\\';
        return null;
    }

    substitutedText = score.@substituteName;
    if (substitutedText = '') {
        // TODO: Also check for all-whitespace text
        return null;
    }

    element = DataToMEI(textSubstituteInfo);
    state.nodes.Push(element);

    styleAttributes = GetStyleAttributes(state);
    rendElement = null;
    if (null = styleAttributes)
    {
        libmei.SetText(element, substitutedText);
    }
    else
    {
        rendElement = libmei.Rend();
        libmei.AddChild(element, rendElement);
        for each Name attName in styleAttributes
        {
            libmei.AddAttribute(rendElement, attName, styleAttributes[attName]);
        }
        libmei.SetText(rendElement, substitutedText);
    }
}  //$end

function InitTextHandlers() {
    // QUESTION: We could also take an argument and throw all text handlers from
    // extensions into the same dictionary

    Self._property:TextHandlers = CreateDictionary(
        'text.staff.expression', 'ExpressionTextHandler',
        'text.system.page_aligned.title', 'PageTitleHandler',
        'text.system.page_aligned.subtitle', 'PageTitleHandler',
        'text.system.page_aligned.composer', 'PageComposerTextHandler',
        'text.system.tempo', 'TempoTextHandler',
        'text.staff.space.figuredbass', 'FiguredBassTextHandler',
        'text.staff.plain', 'CreateAnchoredText'
    );

    Self._property:TextSubstituteMap = CreateDictionary(
        'Title', CreateSparseArray('Title'),
        'Subtitle', CreateSparseArray('Title', CreateDictionary('type', 'subordinate')),
        'Dedication', CreateSparseArray('Dedication'),
        // <composer>, <arranger>, <lyricist>, <userRestrict> and <publisher>
        // are only allowed in a few places, e.g. metadata or title pages.
        // We therfore use more generic elements
        'Composer', CreateSparseArray('PersName', CreateDictionary('role', 'Composer')),
        'Arranger', CreateSparseArray('PersName', CreateDictionary('role', 'Arranger')),
        'Lyricist', CreateSparseArray('PersName', CreateDictionary('role', 'Lyricist')),
        'Artist', CreateSparseArray('PersName', CreateDictionary('role', 'Artist')),
        // <useRestrict> is only allowed on <titlePage>, so use generic element
        'Copyright', CreateSparseArray('Seg', CreateDictionary('type', 'Copyright')),
        // <publisher> is only allowed in a few places, so use generic element
        // We don't even know if it's a person or an institution
        'Publisher', CreateSparseArray('Seg', CreateDictionary('type', 'Publisher')),
        'MoreInfo', CreateSparseArray('Seg', CreateDictionary('type', 'MoreInfo')),
        'PartName', CreateSparseArray('Seg', CreateDictionary('type', 'PartName'))
    );
}  //$end

function HandleText (textObject) {
    // TODO: Move InitTextHandlers() call to a global initialization function
    InitTextHandlers();
    if (null != Self._property:Extension and null != Extension.TextHandlers)
    {
        // TODO: We need to check for both StyleId *and* StyleAsText so we can
        // handle custom styles
        textHandler = Extension.TextHandlers[textObject.StyleId];
        if (null != textHandler)
        {
            return Extension.@textHandler(textObject);
        }
    }

    textHandler = TextHandlers[textObject.StyleId];
    if (null != textHandler)
    {
        return @textHandler(textObject);
    }
}  //$end

function ExpressionTextHandler (textObject) {
    dynam = AddBarObjectInfoToElement(textObject, libmei.Dynam());
    AddFormattedText(dynam, textObject);
    return dynam;
}  //$end

function PageTitleHandler (textObject) {
    anchoredText = libmei.AnchoredText();
    title = libmei.Title();
    if (textObject.StyleId = 'text.system.page_aligned.subtitle')
    {
        libmei.AddAttribute(title, 'type', 'subordinate');
    }

    libmei.AddChild(anchoredText, title);
    AddFormattedText(title, textObject);

    return anchoredText;
}  //$end

function PageComposerTextHandler (textObject) {
    // 'text.system.page_aligned.composer'
    return AddBarObjectInfoToElement(textObject, CreateAnchoredText(textObject));
}  //$end

function TempoTextHandler (textObject) {
    // 'text.system.tempo'
    tempo = libmei.Tempo();
    libmei.AddChild(tempo, CreateAnchoredText(textObject));
    return tempo;
}  //$end

function FiguredBassTextHandler (textObject) {
    // 'text.staff.space.figuredbass'
    harm = AddBarObjectInfoToElement(textObject, libmei.Harm());
    fb = libmei.Fb();
    libmei.AddChild(harm, fb);
    ConvertFbFigures(fb, textObject);
    return harm;
}  //$end

function CreateAnchoredText (textObj) {
    //$module(ExportConverters.mss)
    anchoredText = libmei.AnchoredText();
    AddFormattedText(anchoredText, textObj);
    return anchoredText;
}  //$end

function AddFormattedText (parentElement, textObj) {
    textWithFormatting = textObj.TextWithFormatting;
    if (textWithFormatting.NumChildren < 2 and CharAt(textWithFormatting[0], 0) != '\\')
    {
        libmei.SetText(parentElement, textObj.Text);
        return null;
    }

    nodes = CreateSparseArray();

    state = CreateDictionary(
        'currentText', null,
        'rendAttributes', CreateDictionary(),
        'rendFlags', CreateDictionary(),
        // TODO: Also track the active character style (mainly
        // `\ctext.character.musictext\`, and custom styles)
        'nodes', nodes,
        'paragraphs', null
    );

    for each component in textObj.TextWithFormatting
    {
        switch (Substring(component, 0, 2))
        {
            case ('\\n')
            {
                PushStyledText(state);
                nodes.Push(libmei.Lb());
            }
            case ('\\N')
            {
                PushStyledText(state);
                // TODO: Add <p> if it is allowed within parentElement (use libmei.GetName())
                nodes.Push(libmei.Lb());
            }
            case ('\\B')
            {
                SwitchTextStyle(state, 'fontweight', 'bold');
            }
            case ('\\b')
            {
                SwitchTextStyle(state, 'fontweight', 'normal');
            }
            case ('\\I')
            {
                SwitchTextStyle(state, 'fontstyle', 'italic');
            }
            case ('\\i')
            {
                SwitchTextStyle(state, 'fontstyle', 'normal');
            }
            case ('\\U')
            {
                SwitchTextStyle(state, 'rend', 'underline', true);
            }
            case ('\\u')
            {
                SwitchTextStyle(state, 'rend', 'underline', false);
            }
            case ('\\f')
            {
                SwitchFont(state, GetTextCommandArg(component));
            }
            case ('\\c')
            {
                // TODO: Can we sensibly handle a character style change? The
                // only built-in one seem to be `text.character.musictext`. We
                // might want to allow Extensions to handle custom character
                // styles.
                ;
            }
            case ('\\s')
            {
                fontsize = ConvertOffsetsToMEI(GetTextCommandArg(component));
                SwitchTextStyle(state, 'fontsize', fontsize);
            }
            case ('\\v')
            {
                // Vertical scale change (vertical stretching). Probably not
                // possible to handle
                ;
            }
            case ('\\h')
            {
                // Horizontal scale change (horizontal stretching). Probably not
                // possible to handle
                ;
            }
            case ('\\t')
            {
                // Tracking. Probably not possible to handle.
                ;
            }
            case ('\\p')
            {
                SwitchBaselineAdjust(state, GetTextCommandArg(component));
            }
            case ('\\$') {
                AppendTextSubstitute(state, GetTextCommandArg(component));
            }
            case ('\\\\')
            {
                // According to the documentation, 'backslashes themselves are
                // represented by \\ , to avoid conflicting with the above
                // commands'. Though that does not seem to work, let's just
                // assume it does in case Avid fixes this.

                // We strip one leading backspace.
                state.currentText = state.currentText & Substring(component, 1);
            }
            default
            {
                // This is regular text
                state.currentText = state.currentText & component;
            }
        }
    }

    PushStyledText(state);

    nodeCount = nodes.Length;
    precedingElement = null;

    nodeIndex = 0;
    while (nodeIndex < nodeCount)
    {
        node = nodes[nodeIndex];
        if (IsObject(node))
        {
            // We have an element
            libmei.AddChild(parentElement, node);
            precedingElement = node;
        }
        else
        {
            // We have a text node
            text = node;
            // If there are multiple adjacent text nodes, we need to join them
            while (nodeIndex < nodeCount and not IsObject(nodes[nodeIndex + 1])) {
                nodeIndex = nodeIndex + 1;
                text = text & nodes[nodeIndex];
            }

            if (precedingElement = null)
            {
                libmei.SetText(parentElement, text);
            }
            else
            {
                libmei.SetTail(precedingElement, text);
            }
        }
        nodeIndex = nodeIndex + 1;
    }
}  //$end

function NewTextParagraph (state) {
    // TODO!
    ;
}  //$end

function GetTextCommandArg (command) {
    // Remove leading part, e.g. the '\$' or '\s' and trailing '\'
    return Substring(command, 2, Length(command) - 3);
}  //$end

function SwitchBaselineAdjust (state, param) {
    sup = (param = 'superscript');
    sub = (param = 'subscript');
    if (sup != state.rendFlags['sup'] or sub != state.rendFlags['sub']) {
        // Style changed, push the previous text before changing the style
        PushStyledText(state);
    }
    state.rendFlags['sup'] = sup;
    state.rendFlags['sub'] = sub;
}  //$end

function ResetTextStyles (state, infoOnly) {
    // If `infoOnly` is `true`, does not make any changes, only tells us if
    // there are re-settable styles
    if (null != state.rendAttributes)
    {
        for each Name attName in state.rendAttributes
        {
            if (infoOnly and state.rendAttributes[attName] != null)
            {
                return true;
            }
            state.rendAttributes[attName] = null;
        }
    }

    if (null != state.rendFlags)
    {
        for each Name flagName in state.rendFlags
        {
            if (infoOnly and state.rendFlags[flagName])
            {
                return true;
            }
            state.rendFlags[flagName] = false;
        }
    }
    return false;
}  //$end

function SwitchFont (state, fontName) {
    if (fontName = '_')
    {
        // Before resetting the style, we have to add text preceding the '\f_\'
        // style reset – but only if the style reset actually changes something.
        if (ResetTextStyles(state, true))
        {
            PushStyledText(state);
            ResetTextStyles(state, false);
        }
    }
    else
    {
        SwitchTextStyle(state, 'fontfam', fontName);
    }
}  //$end

function SwitchTextStyle (state, attName, value) {
    if (state.rendAttributes[attName] != value)
    {
        // Style changes, so append current text before modifying style state
        PushStyledText(state);
    }
    state.rendAttributes[attName] = value;
}  //$end

function SwitchRendFlags (state, flagName, value) {
    if (state.rendFlags[flagName] != value)
    {
        PushStyledText(state);
    }
    state.rendFlags[flagName] = value;
}  //$end

function PushStyledText (state) {
    if (state.currentText = '')
    {
        return null;
    }

    styleAttributes = GetStyleAttributes(state);
    if (null = styleAttributes)
    {
        // We attach unstyled text without wrapping it in <rend>
        state.nodes.Push(state.currentText);
    }
    else
    {
        rend = libmei.Rend();
        for each Name attName in styleAttributes {
            libmei.AddAttribute(rend, attName, styleAttributes[attName]);
        }
        libmei.SetText(rend, state.currentText);
        state.nodes.Push(rend);
    }

    state.currentText = '';
}  //$end

function GetStyleAttributes (state) {
    rendAttributes = null;

    if (null != state.rendAttributes)
    {
        for each Name attName in state.rendAttributes
        {
            value = state.rendAttributes[attName];
            if (null != value)
            {
                if (null = rendAttributes) {
                    rendAttributes = CreateDictionary();
                }
                rendAttributes[attName] = value;
            }
        }
    }

    if (null != state.rendFlags)
    {
        rendAttValue = '';
        firstRendFlag = true;
        for each Name flagName in state.rendFlags
        {
            flagActive = state.rendFlags[flagName];
            if (flagActive)
            {
                if (firstRendFlag = true)
                {
                    rendAttValue = rendAttValue & flagName;
                    firstRendFlag = false;
                }
                else
                {
                    rendAttValue = rendAttValue & ' ' & flagName;
                }
            }
        }
        if (rendAttValue != '')
        {
            if (null = rendAttributes) {
                rendAttributes = CreateDictionary();
            }
            rendAttributes['rend'] = rendAttValue;
        }
    }

    return rendAttributes;
}  //$end

function AppendTextSubstitute (state, substituteName) {
    score = Self._property:ActiveScore;

    textSubstituteInfo = TextSubstituteMap[substituteName];
    if (null = textSubstituteInfo)
    {
        // No known substitution. Sibelius renders those literally.
        state.currentText = state.currentText & '\\$' & substituteName & '\\';
        return null;
    }

    substitutedText = score.@substituteName;
    if (substitutedText = '') {
        // TODO: Also check for all-whitespace text
        return null;
    }

    element = DataToMEI(textSubstituteInfo);
    state.nodes.Push(element);

    styleAttributes = GetStyleAttributes(state);
    rendElement = null;
    if (null = styleAttributes)
    {
        libmei.SetText(element, substitutedText);
    }
    else
    {
        rendElement = libmei.Rend();
        libmei.AddChild(element, rendElement);
        for each Name attName in styleAttributes
        {
            libmei.AddAttribute(rendElement, attName, styleAttributes[attName]);
        }
        libmei.SetText(rendElement, substitutedText);
    }
}  //$end