diff --git a/aegisub/LICENCE b/aegisub/LICENCE
index 83d7c2391bb46dc23248ab10148f41e82aa84420..db85fd642f47ac06a1d8317d42540505a2d7a9b4 100644
--- a/aegisub/LICENCE
+++ b/aegisub/LICENCE
@@ -34,7 +34,7 @@ The following directories and file are covered by their respective licenses as
 follows:
 
 libass/
- - GPL see libass/COPYING.
+ - ISC license. See libass/COPYING.
 
 libffms/
  - MIT license.
diff --git a/aegisub/libass/COPYING b/aegisub/libass/COPYING
index d511905c1647a1e311e8b20d5930a37a9c2531cd..8351a30e3a00ed7525805a642bb374d3130fec12 100644
--- a/aegisub/libass/COPYING
+++ b/aegisub/libass/COPYING
@@ -1,339 +1,11 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-			    Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users.  This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it.  (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.)  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
-  To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have.  You must make sure that they, too, receive or can get the
-source code.  And you must show them these terms so they know their
-rights.
-
-  We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
-  Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software.  If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
-  Finally, any free program is threatened constantly by software
-patents.  We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary.  To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License.  The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language.  (Hereinafter, translation is included without limitation in
-the term "modification".)  Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope.  The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
-  1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
-  2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
-    a) You must cause the modified files to carry prominent notices
-    stating that you changed the files and the date of any change.
-
-    b) You must cause any work that you distribute or publish, that in
-    whole or in part contains or is derived from the Program or any
-    part thereof, to be licensed as a whole at no charge to all third
-    parties under the terms of this License.
-
-    c) If the modified program normally reads commands interactively
-    when run, you must cause it, when started running for such
-    interactive use in the most ordinary way, to print or display an
-    announcement including an appropriate copyright notice and a
-    notice that there is no warranty (or else, saying that you provide
-    a warranty) and that users may redistribute the program under
-    these conditions, and telling the user how to view a copy of this
-    License.  (Exception: if the Program itself is interactive but
-    does not normally print such an announcement, your work based on
-    the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole.  If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works.  But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
-  3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
-    a) Accompany it with the complete corresponding machine-readable
-    source code, which must be distributed under the terms of Sections
-    1 and 2 above on a medium customarily used for software interchange; or,
-
-    b) Accompany it with a written offer, valid for at least three
-    years, to give any third party, for a charge no more than your
-    cost of physically performing source distribution, a complete
-    machine-readable copy of the corresponding source code, to be
-    distributed under the terms of Sections 1 and 2 above on a medium
-    customarily used for software interchange; or,
-
-    c) Accompany it with the information you received as to the offer
-    to distribute corresponding source code.  (This alternative is
-    allowed only for noncommercial distribution and only if you
-    received the program in object code or executable form with such
-    an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it.  For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable.  However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
-  4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License.  Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-  5. You are not required to accept this License, since you have not
-signed it.  However, nothing else grants you permission to modify or
-distribute the Program or its derivative works.  These actions are
-prohibited by law if you do not accept this License.  Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
-  6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions.  You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
-  7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all.  For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices.  Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
-  8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded.  In such case, this License incorporates
-the limitation as if written in the body of this License.
-
-  9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation.  If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
-  10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission.  For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this.  Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
-			    NO WARRANTY
-
-  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
-  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License along
-    with this program; if not, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
-    Gnomovision version 69, Copyright (C) year name of author
-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary.  Here is a sample; alter the names:
-
-  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-  `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-  <signature of Ty Coon>, 1 April 1989
-  Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs.  If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/aegisub/libass/Makefile.am b/aegisub/libass/Makefile.am
index 8a990787e2d41bc3a092015fd6a58f1aa7926f57..a5705da1f84befea57a63ac907b1065d6e84f415 100644
--- a/aegisub/libass/Makefile.am
+++ b/aegisub/libass/Makefile.am
@@ -16,6 +16,7 @@ libass_aegisub_a_SOURCES = \
 	ass_library.c \
 	ass_parse.c \
 	ass_render.c \
+	ass_render_api.c \
 	ass_strtod.c \
 	ass_utils.c
 
diff --git a/aegisub/libass/ass.c b/aegisub/libass/ass.c
index 6becb39e8e990536f78d7b6b7309d94b669da292..368377251e486cda276ef96f7374440c87d16320 100644
--- a/aegisub/libass/ass.c
+++ b/aegisub/libass/ass.c
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include "config.h"
@@ -23,6 +21,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 #include <assert.h>
 #include <errno.h>
 #include <sys/types.h>
@@ -38,6 +37,8 @@
 #include "ass_utils.h"
 #include "ass_library.h"
 
+#define ass_atof(STR) (ass_strtod((STR),NULL))
+
 typedef enum {
     PST_UNKNOWN = 0,
     PST_INFO,
@@ -62,26 +63,22 @@ void ass_free_track(ASS_Track *track)
     int i;
 
     if (track->parser_priv) {
-        if (track->parser_priv->fontname)
-            free(track->parser_priv->fontname);
-        if (track->parser_priv->fontdata)
-            free(track->parser_priv->fontdata);
+        free(track->parser_priv->fontname);
+        free(track->parser_priv->fontdata);
         free(track->parser_priv);
     }
-    if (track->style_format)
-        free(track->style_format);
-    if (track->event_format)
-        free(track->event_format);
+    free(track->style_format);
+    free(track->event_format);
     if (track->styles) {
         for (i = 0; i < track->n_styles; ++i)
             ass_free_style(track, i);
-        free(track->styles);
     }
+    free(track->styles);
     if (track->events) {
         for (i = 0; i < track->n_events; ++i)
             ass_free_event(track, i);
-        free(track->events);
     }
+    free(track->events);
     free(track->name);
     free(track);
 }
@@ -133,23 +130,19 @@ int ass_alloc_event(ASS_Track *track)
 void ass_free_event(ASS_Track *track, int eid)
 {
     ASS_Event *event = track->events + eid;
-    if (event->Name)
-        free(event->Name);
-    if (event->Effect)
-        free(event->Effect);
-    if (event->Text)
-        free(event->Text);
-    if (event->render_priv)
-        free(event->render_priv);
+
+    free(event->Name);
+    free(event->Effect);
+    free(event->Text);
+    free(event->render_priv);
 }
 
 void ass_free_style(ASS_Track *track, int sid)
 {
     ASS_Style *style = track->styles + sid;
-    if (style->Name)
-        free(style->Name);
-    if (style->FontName)
-        free(style->FontName);
+
+    free(style->Name);
+    free(style->FontName);
 }
 
 // ==============================================================================================
@@ -250,7 +243,7 @@ static int numpad2align(int val)
 		ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token);
 
 #define INTVAL(name) ANYVAL(name,atoi)
-#define FPVAL(name) ANYVAL(name,atof)
+#define FPVAL(name) ANYVAL(name,ass_atof)
 #define TIMEVAL(name) \
 	} else if (strcasecmp(tname, #name) == 0) { \
 		target->name = string2timecode(track->library, token); \
@@ -384,7 +377,7 @@ void ass_process_force_style(ASS_Track *track)
         else if (!strcasecmp(*fs, "PlayResY"))
             track->PlayResY = atoi(token);
         else if (!strcasecmp(*fs, "Timer"))
-            track->Timer = atof(token);
+            track->Timer = ass_atof(token);
         else if (!strcasecmp(*fs, "WrapStyle"))
             track->WrapStyle = atoi(token);
         else if (!strcasecmp(*fs, "ScaledBorderAndShadow"))
@@ -534,12 +527,6 @@ static int process_style(ASS_Track *track, char *str)
         style->Name = strdup("Default");
     if (!style->FontName)
         style->FontName = strdup("Arial");
-    // skip '@' at the start of the font name
-    if (*style->FontName == '@') {
-        p = style->FontName;
-        style->FontName = strdup(p + 1);
-        free(p);
-    }
     free(format);
     return 0;
 
@@ -568,7 +555,7 @@ static int process_info_line(ASS_Track *track, char *str)
     } else if (!strncmp(str, "PlayResY:", 9)) {
         track->PlayResY = atoi(str + 9);
     } else if (!strncmp(str, "Timer:", 6)) {
-        track->Timer = atof(str + 6);
+        track->Timer = ass_atof(str + 6);
     } else if (!strncmp(str, "WrapStyle:", 10)) {
         track->WrapStyle = atoi(str + 10);
     } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) {
@@ -597,6 +584,7 @@ static int process_events_line(ASS_Track *track, char *str)
     if (!strncmp(str, "Format:", 7)) {
         char *p = str + 7;
         skip_spaces(&p);
+        free(track->event_format);
         track->event_format = strdup(p);
         ass_msg(track->library, MSGL_DBG2, "Event format: %s", track->event_format);
     } else if (!strncmp(str, "Dialogue:", 9)) {
@@ -618,7 +606,7 @@ static int process_events_line(ASS_Track *track, char *str)
 
         process_event_tail(track, event, str, 0);
     } else {
-        ass_msg(track->library, MSGL_V, "Not understood: '%s'", str);
+        ass_msg(track->library, MSGL_V, "Not understood: '%.30s'", str);
     }
     return 0;
 }
@@ -677,12 +665,10 @@ static int decode_font(ASS_Track *track)
     if (track->library->extract_fonts) {
         ass_add_font(track->library, track->parser_priv->fontname,
                      (char *) buf, dsize);
-        buf = 0;
     }
 
-  error_decode_font:
-    if (buf)
-        free(buf);
+error_decode_font:
+    free(buf);
     free(track->parser_priv->fontname);
     free(track->parser_priv->fontdata);
     track->parser_priv->fontname = 0;
@@ -909,6 +895,20 @@ void ass_process_chunk(ASS_Track *track, char *data, int size,
     free(str);
 }
 
+/**
+ * \brief Flush buffered events.
+ * \param track track
+*/
+void ass_flush_events(ASS_Track *track)
+{
+    if (track->events) {
+        int eid;
+        for (eid = 0; eid < track->n_events; eid++)
+            ass_free_event(track, eid);
+        track->n_events = 0;
+    }
+}
+
 #ifdef CONFIG_ICONV
 /** \brief recode buffer to utf-8
  * constraint: codepage != 0
@@ -1019,14 +1019,6 @@ static char *read_file(ASS_Library *library, char *fname, size_t *bufsize)
     sz = ftell(fp);
     rewind(fp);
 
-    if (sz > 10 * 1024 * 1024) {
-        ass_msg(library, MSGL_INFO,
-               "ass_read_file(%s): Refusing to load subtitles "
-               "larger than 10MiB", fname);
-        fclose(fp);
-        return 0;
-    }
-
     ass_msg(library, MSGL_V, "File size: %ld", sz);
 
     buf = malloc(sz + 1);
diff --git a/aegisub/libass/ass.h b/aegisub/libass/ass.h
index e7674a736cab11a265aba62f7444cdbd85fcf24f..3762bffe448a102db28c2906b268bf8fd81c824e 100644
--- a/aegisub/libass/ass.h
+++ b/aegisub/libass/ass.h
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_ASS_H
@@ -25,7 +23,7 @@
 #include <stdarg.h>
 #include "ass_types.h"
 
-#define LIBASS_VERSION 0x00908000
+#define LIBASS_VERSION 0x00911000
 
 /*
  * A linked list of images produced by an ass renderer.
@@ -75,11 +73,13 @@ ASS_Library *ass_library_init(void);
 void ass_library_done(ASS_Library *priv);
 
 /**
- * \brief Set private font directory.
- * It is used for saving embedded fonts and also in font lookup.
+ * \brief Set additional fonts directory.
+ * Optional directory that will be scanned for fonts recursively.  The fonts
+ * found are used for font lookup.
+ * NOTE: A valid font directory is not needed to support embedded fonts.
  *
  * \param priv library handle
- * \param fonts_dir private directory for font extraction
+ * \param fonts_dir directory with additional fonts
  */
 void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir);
 
@@ -203,6 +203,8 @@ void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing);
  * if fontconfig is used.
  * \param update whether fontconfig cache should be built/updated now.  Only
  * relevant if fontconfig is used.
+ *
+ * NOTE: font lookup must be configured before an ASS_Renderer can be used.
  */
 void ass_set_fonts(ASS_Renderer *priv, const char *default_font,
                    const char *default_family, int fc, const char *config,
@@ -297,7 +299,8 @@ void ass_free_event(ASS_Track *track, int eid);
 void ass_process_data(ASS_Track *track, char *data, int size);
 
 /**
- * \brief Parse Codec Private section of subtitle stream.
+ * \brief Parse Codec Private section of the subtitle stream, in Matroska
+ * format.  See the Matroska specification for details.
  * \param track target track
  * \param data string to parse
  * \param size length of data
@@ -305,8 +308,8 @@ void ass_process_data(ASS_Track *track, char *data, int size);
 void ass_process_codec_private(ASS_Track *track, char *data, int size);
 
 /**
- * \brief Parse a chunk of subtitle stream data. In Matroska,
- * this contains exactly 1 event (or a commentary).
+ * \brief Parse a chunk of subtitle stream data. A chunk contains exactly one
+ * event in Matroska format.  See the Matroska specification for details.
  * \param track track
  * \param data string to parse
  * \param size length of data
@@ -316,6 +319,12 @@ void ass_process_codec_private(ASS_Track *track, char *data, int size);
 void ass_process_chunk(ASS_Track *track, char *data, int size,
                        long long timecode, long long duration);
 
+/**
+ * \brief Flush buffered events.
+ * \param track track
+*/
+void ass_flush_events(ASS_Track *track);
+
 /**
  * \brief Read subtitles from file.
  * \param library library handle
diff --git a/aegisub/libass/ass_bitmap.c b/aegisub/libass/ass_bitmap.c
index c7c039ddd5eb2ce10c936dca3dcf1f5478ee41e0..6ecdbc6678de3187bf6c064fc6c32cb96349823c 100644
--- a/aegisub/libass/ass_bitmap.c
+++ b/aegisub/libass/ass_bitmap.c
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include <stdlib.h>
@@ -113,8 +111,7 @@ static void resize_tmp(ASS_SynthPriv *priv, int w, int h)
         priv->tmp_w *= 2;
     while (priv->tmp_h < h)
         priv->tmp_h *= 2;
-    if (priv->tmp)
-        free(priv->tmp);
+    free(priv->tmp);
     priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short));
 }
 
@@ -127,20 +124,17 @@ ASS_SynthPriv *ass_synth_init(double radius)
 
 void ass_synth_done(ASS_SynthPriv *priv)
 {
-    if (priv->tmp)
-        free(priv->tmp);
-    if (priv->g)
-        free(priv->g);
-    if (priv->gt2)
-        free(priv->gt2);
+    free(priv->tmp);
+    free(priv->g);
+    free(priv->gt2);
     free(priv);
 }
 
 static Bitmap *alloc_bitmap(int w, int h)
 {
     Bitmap *bm;
-    bm = calloc(1, sizeof(Bitmap));
-    bm->buffer = malloc(w * h);
+    bm = malloc(sizeof(Bitmap));
+    bm->buffer = calloc(w, h);
     bm->w = w;
     bm->h = h;
     bm->left = bm->top = 0;
@@ -149,11 +143,9 @@ static Bitmap *alloc_bitmap(int w, int h)
 
 void ass_free_bitmap(Bitmap *bm)
 {
-    if (bm) {
-        if (bm->buffer)
-            free(bm->buffer);
-        free(bm);
-    }
+    if (bm)
+        free(bm->buffer);
+    free(bm);
 }
 
 static Bitmap *copy_bitmap(const Bitmap *src)
@@ -165,7 +157,7 @@ static Bitmap *copy_bitmap(const Bitmap *src)
     return dst;
 }
 
-static int check_glyph_area(ASS_Library *library, FT_Glyph glyph)
+int check_glyph_area(ASS_Library *library, FT_Glyph glyph)
 {
     FT_BBox bbox;
     long long dx, dy;
@@ -213,7 +205,6 @@ static Bitmap *glyph_to_bitmap_internal(ASS_Library *library,
     w = bit->width;
     h = bit->rows;
     bm = alloc_bitmap(w + 2 * bord, h + 2 * bord);
-    memset(bm->buffer, 0, bm->w * bm->h);
     bm->left = bg->left - bord;
     bm->top = -bg->top - bord;
 
diff --git a/aegisub/libass/ass_bitmap.h b/aegisub/libass/ass_bitmap.h
index 338db011937871e65795a45c296eeff011b0f323..287b638289d835431f1564ea14f29a2ed3583a8e 100644
--- a/aegisub/libass/ass_bitmap.h
+++ b/aegisub/libass/ass_bitmap.h
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_BITMAP_H
@@ -53,5 +51,6 @@ int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *priv_blur,
                     int border_style);
 
 void ass_free_bitmap(Bitmap *bm);
+int check_glyph_area(ASS_Library *library, FT_Glyph glyph);
 
 #endif                          /* LIBASS_BITMAP_H */
diff --git a/aegisub/libass/ass_cache.c b/aegisub/libass/ass_cache.c
index 643d9912ed2d2819a091a8a5bd345223a304f951..46c2478f8dfd6b42b29a7b85c037a9a216f14e62 100644
--- a/aegisub/libass/ass_cache.c
+++ b/aegisub/libass/ass_cache.c
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include "config.h"
@@ -156,6 +154,8 @@ static int font_compare(void *key1, void *key2, size_t key_size)
         return 0;
     if (a->treat_family_as_pattern != b->treat_family_as_pattern)
         return 0;
+    if (a->vertical != b->vertical)
+        return 0;
     return 1;
 }
 
@@ -286,6 +286,11 @@ static void glyph_hash_dtor(void *key, size_t key_size, void *value,
 void *cache_add_glyph(Hashmap *glyph_cache, GlyphHashKey *key,
                       GlyphHashValue *val)
 {
+	if (val->glyph && val->glyph->format == FT_GLYPH_FORMAT_BITMAP) {
+		FT_Bitmap *bitmap = &((FT_BitmapGlyph) val->glyph)->bitmap;
+		glyph_cache->cache_size += bitmap->rows * bitmap->pitch;
+	}
+
     return hashmap_insert(glyph_cache, key, val);
 }
 
diff --git a/aegisub/libass/ass_cache.h b/aegisub/libass/ass_cache.h
index 5c9749f87092978f64c08878b210776ee1afb1f9..472bf359bedb6978b8d2c07c22e9542087090584 100644
--- a/aegisub/libass/ass_cache.h
+++ b/aegisub/libass/ass_cache.h
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_CACHE_H
diff --git a/aegisub/libass/ass_drawing.c b/aegisub/libass/ass_drawing.c
index a3207c7c3b113e0217a6e399499d04f768845ab0..93cc458d1683e5ad97d850b105f5fa43762b173c 100644
--- a/aegisub/libass/ass_drawing.c
+++ b/aegisub/libass/ass_drawing.c
@@ -34,13 +34,13 @@
  * \brief Get and prepare a FreeType glyph
  */
 static void drawing_make_glyph(ASS_Drawing *drawing, void *fontconfig_priv,
-                               ASS_Font *font, ASS_Hinting hint)
+                               ASS_Font *font)
 {
     FT_OutlineGlyph glyph;
 
     // This is hacky...
     glyph = (FT_OutlineGlyph) ass_font_get_glyph(fontconfig_priv, font,
-                                                 (uint32_t) ' ', hint, 0);
+                                                 (uint32_t) ' ', 0, 0);
     if (glyph) {
         FT_Outline_Done(drawing->ftlibrary, &glyph->outline);
         FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS,
@@ -112,24 +112,12 @@ static void drawing_prepare(ASS_Drawing *drawing)
 static void drawing_finish(ASS_Drawing *drawing, int raw_mode)
 {
     int i, offset;
-    FT_BBox bbox;
+    FT_BBox bbox = drawing->cbox;
     FT_Outline *ol = &drawing->glyph->outline;
 
     // Close the last contour
     drawing_close_shape(drawing);
 
-#if 0
-    // Dump points
-    for (i = 0; i < ol->n_points; i++) {
-        printf("point (%d, %d)\n", (int) ol->points[i].x,
-               (int) ol->points[i].y);
-    }
-
-    // Dump contours
-    for (i = 0; i < ol->n_contours; i++)
-        printf("contour %d\n", ol->contours[i]);
-#endif
-
     ass_msg(drawing->library, MSGL_V,
             "Parsed drawing with %d points and %d contours", ol->n_points,
             ol->n_contours);
@@ -137,7 +125,6 @@ static void drawing_finish(ASS_Drawing *drawing, int raw_mode)
     if (raw_mode)
         return;
 
-    FT_Outline_Get_CBox(&drawing->glyph->outline, &bbox);
     drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin);
 
     drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y);
@@ -231,15 +218,6 @@ static ASS_DrawingToken *drawing_tokenize(char *str)
         p++;
     }
 
-#if 0
-    // Check tokens
-    ASS_DrawingToken *t = root;
-    while(t) {
-        printf("token %d point (%d, %d)\n", t->type, t->point.x, t->point.y);
-        t = t->next;
-    }
-#endif
-
     return root;
 }
 
@@ -255,6 +233,19 @@ static void drawing_free_tokens(ASS_DrawingToken *token)
     }
 }
 
+/*
+ * \brief Update drawing cbox
+ */
+static inline void update_cbox(ASS_Drawing *drawing, FT_Vector *point)
+{
+    FT_BBox *box = &drawing->cbox;
+
+    box->xMin = FFMIN(box->xMin, point->x);
+    box->xMax = FFMAX(box->xMax, point->x);
+    box->yMin = FFMIN(box->yMin, point->y);
+    box->yMax = FFMAX(box->yMax, point->y);
+}
+
 /*
  * \brief Translate and scale a point coordinate according to baseline
  * offset and scale.
@@ -263,6 +254,8 @@ static inline void translate_point(ASS_Drawing *drawing, FT_Vector *point)
 {
     point->x = drawing->point_scale_x * point->x;
     point->y = drawing->point_scale_y * -point->y;
+
+    update_cbox(drawing, point);
 }
 
 /*
@@ -362,19 +355,19 @@ static void drawing_evaluate_curve(ASS_Drawing *drawing,
  * \brief Create and initialize a new drawing and return it
  */
 ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font,
-                             ASS_Hinting hint, FT_Library lib)
+                             FT_Library lib)
 {
     ASS_Drawing *drawing;
 
     drawing = calloc(1, sizeof(*drawing));
     drawing->text = calloc(1, DRAWING_INITIAL_SIZE);
     drawing->size = DRAWING_INITIAL_SIZE;
-
+    drawing->cbox.xMin = drawing->cbox.yMin = INT_MAX;
+    drawing->cbox.xMax = drawing->cbox.yMax = INT_MIN;
+    drawing->fontconfig_priv = fontconfig_priv;
+    drawing->font = font;
     drawing->ftlibrary = lib;
-    if (font) {
-        drawing->library = font->library;
-        drawing_make_glyph(drawing, fontconfig_priv, font, hint);
-    }
+    drawing->library = font->library;
 
     drawing->scale_x = 1.;
     drawing->scale_y = 1.;
@@ -390,8 +383,6 @@ ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font,
 void ass_drawing_free(ASS_Drawing* drawing)
 {
     if (drawing) {
-        if (drawing->glyph)
-            FT_Done_Glyph((FT_Glyph) drawing->glyph);
         free(drawing->text);
     }
     free(drawing);
@@ -429,6 +420,8 @@ FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode)
     ASS_DrawingToken *token;
     FT_Vector pen = {0, 0};
 
+    if (drawing->font)
+        drawing_make_glyph(drawing, drawing->fontconfig_priv, drawing->font);
     if (!drawing->glyph)
         return NULL;
 
diff --git a/aegisub/libass/ass_drawing.h b/aegisub/libass/ass_drawing.h
index 913588e74deeedc3c3360cb35db594386fcd07f8..f677fcdddc26a52be0b037528217f04f88687afb 100644
--- a/aegisub/libass/ass_drawing.h
+++ b/aegisub/libass/ass_drawing.h
@@ -57,7 +57,9 @@ typedef struct {
     int hash;           // hash value (for caching)
 
     // private
-    FT_Library ftlibrary;   // FT library instance, needed for font ops
+    FT_Library ftlibrary;   // needed for font ops
+    ASS_Font *font;         // dito
+    void *fontconfig_priv;  // dito
     ASS_Library *library;
     int size;           // current buffer size
     ASS_DrawingToken *tokens;    // tokenized drawing
@@ -65,10 +67,11 @@ typedef struct {
     int max_contours;
     double point_scale_x;
     double point_scale_y;
+    FT_BBox cbox;   // bounding box, or let's say... VSFilter's idea of it
 } ASS_Drawing;
 
 ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font,
-                             ASS_Hinting hint, FT_Library lib);
+                             FT_Library lib);
 void ass_drawing_free(ASS_Drawing* drawing);
 void ass_drawing_add_char(ASS_Drawing* drawing, char symbol);
 void ass_drawing_hash(ASS_Drawing* drawing);
diff --git a/aegisub/libass/ass_font.c b/aegisub/libass/ass_font.c
index 7db1f076f7b3988fae04eab4c67372adfb7ccefe..74467df6b03aaebea00dbe2811aa3733685a3f2d 100644
--- a/aegisub/libass/ass_font.c
+++ b/aegisub/libass/ass_font.c
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include "config.h"
@@ -27,6 +25,7 @@
 #include FT_GLYPH_H
 #include FT_TRUETYPE_TABLES_H
 #include FT_OUTLINE_H
+#include <strings.h>
 
 #include "ass.h"
 #include "ass_library.h"
@@ -36,13 +35,18 @@
 #include "ass_fontconfig.h"
 #include "ass_utils.h"
 
+#define VERTICAL_LOWER_BOUND 0x02f1
+
 /**
- * Select Microfost Unicode CharMap, if the font has one.
+ * Select a good charmap, prefer Microsoft Unicode charmaps.
  * Otherwise, let FreeType decide.
  */
 static void charmap_magic(ASS_Library *library, FT_Face face)
 {
     int i;
+    int ms_cmap = -1;
+
+    // Search for a Microsoft Unicode cmap
     for (i = 0; i < face->num_charmaps; ++i) {
         FT_CharMap cmap = face->charmaps[i];
         unsigned pid = cmap->platform_id;
@@ -52,7 +56,15 @@ static void charmap_magic(ASS_Library *library, FT_Face face)
                 || eid == 10 /*full unicode */ )) {
             FT_Set_Charmap(face, cmap);
             return;
-        }
+        } else if (pid == 3 && ms_cmap < 0)
+            ms_cmap = i;
+    }
+
+    // Try the first Microsoft cmap if no Microsoft Unicode cmap was found
+    if (ms_cmap >= 0) {
+        FT_CharMap cmap = face->charmaps[ms_cmap];
+        FT_Set_Charmap(face, cmap);
+        return;
     }
 
     if (!face->charmap) {
@@ -67,17 +79,6 @@ static void charmap_magic(ASS_Library *library, FT_Face face)
     }
 }
 
-static void update_transform(ASS_Font *font)
-{
-    int i;
-    FT_Matrix m;
-    m.xx = double_to_d16(font->scale_x);
-    m.yy = double_to_d16(font->scale_y);
-    m.xy = m.yx = 0;
-    for (i = 0; i < font->n_faces; ++i)
-        FT_Set_Transform(font->faces[i], &m, &font->v);
-}
-
 /**
  * \brief find a memory font by name
  */
@@ -139,7 +140,7 @@ static int add_face(void *fc_priv, ASS_Font *font, uint32_t ch)
             FT_New_Memory_Face(font->ftlibrary,
                                (unsigned char *) font->library->
                                fontdata[mem_idx].data,
-                               font->library->fontdata[mem_idx].size, 0,
+                               font->library->fontdata[mem_idx].size, index,
                                &face);
         if (error) {
             ass_msg(font->library, MSGL_WARN,
@@ -160,7 +161,6 @@ static int add_face(void *fc_priv, ASS_Font *font, uint32_t ch)
     buggy_font_workaround(face);
 
     font->faces[font->n_faces++] = face;
-    update_transform(font);
     face_set_size(face, font->size);
     free(path);
     return font->n_faces - 1;
@@ -188,6 +188,7 @@ ASS_Font *ass_font_new(void *font_cache, ASS_Library *library,
     font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
     font.desc.bold = desc->bold;
     font.desc.italic = desc->italic;
+    font.desc.vertical = desc->vertical;
 
     font.scale_x = font.scale_y = 1.;
     font.v.x = font.v.y = 0;
@@ -213,7 +214,6 @@ void ass_font_set_transform(ASS_Font *font, double scale_x,
         font->v.x = v->x;
         font->v.y = v->y;
     }
-    update_transform(font);
 }
 
 static void face_set_size(FT_Face face, double size)
@@ -276,6 +276,9 @@ void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc,
                 *asc = FT_MulFix(face->ascender, y_scale);
                 *desc = FT_MulFix(-face->descender, y_scale);
             }
+            if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) {
+                *asc = FT_MulFix(face->max_advance_width, y_scale);
+            }
             return;
         }
     }
@@ -414,6 +417,7 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font,
     FT_Glyph glyph;
     FT_Face face = 0;
     int flags = 0;
+    int vertical = font->desc.vertical;
 
     if (ch < 0x20)
         return 0;
@@ -441,6 +445,14 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font,
         if (face_idx >= 0) {
             face = font->faces[face_idx];
             index = FT_Get_Char_Index(face, ch);
+            if (index == 0 && face->num_charmaps > 0) {
+                ass_msg(font->library, MSGL_WARN,
+                    "Glyph 0x%X not found, falling back to first charmap", ch);
+                FT_CharMap cur = face->charmap;
+                FT_Set_Charmap(face, face->charmaps[0]);
+                index = FT_Get_Char_Index(face, ch);
+                FT_Set_Charmap(face, cur);
+            }
             if (index == 0) {
                 ass_msg(font->library, MSGL_ERR,
                         "Glyph 0x%X not found in font for (%s, %d, %d)",
@@ -451,22 +463,23 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font,
     }
 #endif
 
+    flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
+            | FT_LOAD_IGNORE_TRANSFORM;
     switch (hinting) {
     case ASS_HINTING_NONE:
-        flags = FT_LOAD_NO_HINTING;
+        flags |= FT_LOAD_NO_HINTING;
         break;
     case ASS_HINTING_LIGHT:
-        flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
+        flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
         break;
     case ASS_HINTING_NORMAL:
-        flags = FT_LOAD_FORCE_AUTOHINT;
+        flags |= FT_LOAD_FORCE_AUTOHINT;
         break;
     case ASS_HINTING_NATIVE:
-        flags = 0;
         break;
     }
 
-    error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags);
+    error = FT_Load_Glyph(face, index, flags);
     if (error) {
         ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d",
                 index);
@@ -488,6 +501,24 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font,
         return 0;
     }
 
+    // Rotate glyph, if needed
+    if (vertical && ch >= VERTICAL_LOWER_BOUND) {
+        FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 };
+        FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m);
+        FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline,
+                             face->glyph->metrics.vertAdvance,
+                             0);
+        glyph->advance.x = face->glyph->linearVertAdvance;
+    }
+
+    // Apply scaling and shift
+    FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0,
+                        double_to_d16(font->scale_y) };
+    FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline;
+    FT_Outline_Transform(outl, &scale);
+    FT_Outline_Translate(outl, font->v.x, font->v.y);
+    glyph->advance.x *= font->scale_x;
+
     ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE,
                              deco & DECO_STRIKETHROUGH);
 
@@ -502,6 +533,9 @@ FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2)
     FT_Vector v = { 0, 0 };
     int i;
 
+    if (font->desc.vertical)
+        return v;
+
     for (i = 0; i < font->n_faces; ++i) {
         FT_Face face = font->faces[i];
         int i1 = FT_Get_Char_Index(face, c1);
@@ -526,7 +560,133 @@ void ass_font_free(ASS_Font *font)
     for (i = 0; i < font->n_faces; ++i)
         if (font->faces[i])
             FT_Done_Face(font->faces[i]);
-    if (font->desc.family)
-        free(font->desc.family);
+    free(font->desc.family);
     free(font);
 }
+
+/**
+ * \brief Calculate the cbox of a series of points
+ */
+static void
+get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end)
+{
+    box->xMin = box->yMin = INT_MAX;
+    box->xMax = box->yMax = INT_MIN;
+    int i;
+
+    for (i = start; i <= end; i++) {
+        box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin;
+        box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax;
+        box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin;
+        box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax;
+    }
+}
+
+/**
+ * \brief Determine winding direction of a contour
+ * \return direction; 0 = clockwise
+ */
+static int get_contour_direction(FT_Vector *points, int start, int end)
+{
+    int i;
+    long long sum = 0;
+    int x = points[start].x;
+    int y = points[start].y;
+    for (i = start + 1; i <= end; i++) {
+        sum += x * (points[i].y - y) - y * (points[i].x - x);
+        x = points[i].x;
+        y = points[i].y;
+    }
+    sum += x * (points[start].y - y) - y * (points[start].x - x);
+    return sum > 0;
+}
+
+/**
+ * \brief Fix-up stroker result for huge borders by removing inside contours
+ * that would reverse in size
+ */
+void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y)
+{
+    int nc = glyph->outline.n_contours;
+    int begin, stop;
+    char modified = 0;
+    char *valid_cont = malloc(nc);
+    int start = 0;
+    int end = -1;
+    FT_BBox *boxes = malloc(nc * sizeof(FT_BBox));
+    int i, j;
+    int inside_direction;
+
+    inside_direction = FT_Outline_Get_Orientation(&glyph->outline) ==
+        FT_ORIENTATION_TRUETYPE;
+
+    // create a list of cboxes of the contours
+    for (i = 0; i < nc; i++) {
+        start = end + 1;
+        end = glyph->outline.contours[i];
+        get_contour_cbox(&boxes[i], glyph->outline.points, start, end);
+    }
+
+    // for each contour, check direction and whether it's "outside"
+    // or contained in another contour
+    end = -1;
+    for (i = 0; i < nc; i++) {
+        start = end + 1;
+        end = glyph->outline.contours[i];
+        int dir = get_contour_direction(glyph->outline.points, start, end);
+        valid_cont[i] = 1;
+        if (dir == inside_direction) {
+            for (j = 0; j < nc; j++) {
+                if (i == j)
+                    continue;
+                if (boxes[i].xMin >= boxes[j].xMin &&
+                    boxes[i].xMax <= boxes[j].xMax &&
+                    boxes[i].yMin >= boxes[j].yMin &&
+                    boxes[i].yMax <= boxes[j].yMax)
+                    goto check_inside;
+            }
+            /* "inside" contour but we can't find anything it could be
+             * inside of - assume the font is buggy and it should be
+             * an "outside" contour, and reverse it */
+            for (j = 0; j < (end + 1 - start) / 2; j++) {
+                FT_Vector temp = glyph->outline.points[start + j];
+                char temp2 = glyph->outline.tags[start + j];
+                glyph->outline.points[start + j] = glyph->outline.points[end - j];
+                glyph->outline.points[end - j] = temp;
+                glyph->outline.tags[start + j] = glyph->outline.tags[end - j];
+                glyph->outline.tags[end - j] = temp2;
+            }
+            dir ^= 1;
+        }
+        check_inside:
+        if (dir == inside_direction) {
+            FT_BBox box;
+            get_contour_cbox(&box, glyph->outline.points, start, end);
+            int width = box.xMax - box.xMin;
+            int height = box.yMax - box.yMin;
+            if (width < border_x * 2 || height < border_y * 2) {
+                valid_cont[i] = 0;
+                modified = 1;
+            }
+        }
+    }
+
+    // zero-out contours that can be removed; much simpler than copying
+    if (modified) {
+        for (i = 0; i < nc; i++) {
+            if (valid_cont[i])
+                continue;
+            begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1;
+            stop = glyph->outline.contours[i];
+            for (j = begin; j <= stop; j++) {
+                glyph->outline.points[j].x = 0;
+                glyph->outline.points[j].y = 0;
+                glyph->outline.tags[j] = 0;
+            }
+        }
+    }
+
+    free(boxes);
+    free(valid_cont);
+}
+
diff --git a/aegisub/libass/ass_font.h b/aegisub/libass/ass_font.h
index ca0c213a093527e24cb0a94be28c76b0d8ddfaaf..ab4054813cbdf296f5a8a67cfd78b85ce3412762 100644
--- a/aegisub/libass/ass_font.h
+++ b/aegisub/libass/ass_font.h
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_FONT_H
@@ -36,6 +34,7 @@ typedef struct {
     unsigned bold;
     unsigned italic;
     int treat_family_as_pattern;
+    int vertical;               // @font vertical layout
 } ASS_FontDesc;
 
 typedef struct {
@@ -62,5 +61,6 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font,
                             uint32_t ch, ASS_Hinting hinting, int flags);
 FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2);
 void ass_font_free(ASS_Font *font);
+void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y);
 
 #endif                          /* LIBASS_FONT_H */
diff --git a/aegisub/libass/ass_fontconfig.c b/aegisub/libass/ass_fontconfig.c
index 2a43694f3872557aa07140475603e4c16bd494ac..2571739ff219f67289763dbb6e22709e153a271d 100644
--- a/aegisub/libass/ass_fontconfig.c
+++ b/aegisub/libass/ass_fontconfig.c
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include "config.h"
@@ -24,6 +22,7 @@
 #include <stdio.h>
 #include <assert.h>
 #include <string.h>
+#include <strings.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <inttypes.h>
@@ -51,6 +50,61 @@ struct fc_instance {
 
 #ifdef CONFIG_FONTCONFIG
 
+/**
+ * \brief Case-insensitive match ASS/SSA font family against full name. (also
+ * known as "name for humans")
+ *
+ * \param lib library instance
+ * \param priv fontconfig instance
+ * \param family font fullname
+ * \param bold weight attribute
+ * \param italic italic attribute
+ * \return font set
+ */
+static FcFontSet *
+match_fullname(ASS_Library *lib, FCInstance *priv, const char *family,
+               unsigned bold, unsigned italic)
+{
+    FcFontSet *sets[2];
+    FcFontSet *result = FcFontSetCreate();
+    int nsets = 0;
+    int i, fi;
+
+    if ((sets[nsets] = FcConfigGetFonts(priv->config, FcSetSystem)))
+        nsets++;
+    if ((sets[nsets] = FcConfigGetFonts(priv->config, FcSetApplication)))
+        nsets++;
+
+    // Run over font sets and patterns and try to match against full name
+    for (i = 0; i < nsets; i++) {
+        FcFontSet *set = sets[i];
+        for (fi = 0; fi < set->nfont; fi++) {
+            FcPattern *pat = set->fonts[fi];
+            char *fullname;
+            int pi = 0, at;
+            FcBool ol;
+            while (FcPatternGetString(pat, FC_FULLNAME, pi++,
+                   (FcChar8 **) &fullname) == FcResultMatch) {
+                if (FcPatternGetBool(pat, FC_OUTLINE, 0, &ol) != FcResultMatch
+                    || ol != FcTrue)
+                    continue;
+                if (FcPatternGetInteger(pat, FC_SLANT, 0, &at) != FcResultMatch
+                    || at < italic)
+                    continue;
+                if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &at) != FcResultMatch
+                    || at < bold)
+                    continue;
+                if (strcasecmp(fullname, family) == 0) {
+                    FcFontSetAdd(result, FcPatternDuplicate(pat));
+                    break;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
 /**
  * \brief Low-level font selection.
  * \param priv private data
@@ -62,7 +116,7 @@ struct fc_instance {
  * \param code: the character that should be present in the font, can be 0
  * \return font file path
 */
-static char *_select_font(ASS_Library *library, FCInstance *priv,
+static char *select_font(ASS_Library *library, FCInstance *priv,
                           const char *family, int treat_family_as_pattern,
                           unsigned bold, unsigned italic, int *index,
                           uint32_t code)
@@ -74,7 +128,7 @@ static char *_select_font(ASS_Library *library, FCInstance *priv,
     FcChar8 *r_family, *r_style, *r_file, *r_fullname;
     FcBool r_outline, r_embolden;
     FcCharSet *r_charset;
-    FcFontSet *fset = NULL;
+    FcFontSet *ffullname = NULL, *fsorted = NULL, *fset = NULL;
     int curf;
     char *retval = NULL;
     int family_cnt = 0;
@@ -126,10 +180,23 @@ static char *_select_font(ASS_Library *library, FCInstance *priv,
     if (!rc)
         goto error;
 
-    fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result);
-    if (!fset)
+    fsorted = FcFontSort(priv->config, pat, FcTrue, NULL, &result);
+    ffullname = match_fullname(library, priv, family, bold, italic);
+    if (!fsorted || !ffullname)
         goto error;
 
+    fset = FcFontSetCreate();
+    for (curf = 0; curf < ffullname->nfont; ++curf) {
+        FcPattern *curp = ffullname->fonts[curf];
+        FcPatternReference(curp);
+        FcFontSetAdd(fset, curp);
+    }
+    for (curf = 0; curf < fsorted->nfont; ++curf) {
+        FcPattern *curp = fsorted->fonts[curf];
+        FcPatternReference(curp);
+        FcFontSetAdd(fset, curp);
+    }
+
     for (curf = 0; curf < fset->nfont; ++curf) {
         FcPattern *curp = fset->fonts[curf];
 
@@ -215,6 +282,10 @@ static char *_select_font(ASS_Library *library, FCInstance *priv,
         FcPatternDestroy(pat);
     if (rpat)
         FcPatternDestroy(rpat);
+    if (fsorted)
+        FcFontSetDestroy(fsorted);
+    if (ffullname)
+        FcFontSetDestroy(ffullname);
     if (fset)
         FcFontSetDestroy(fset);
     return retval;
@@ -244,11 +315,11 @@ char *fontconfig_select(ASS_Library *library, FCInstance *priv,
     }
     if (family && *family)
         res =
-            _select_font(library, priv, family, treat_family_as_pattern,
+            select_font(library, priv, family, treat_family_as_pattern,
                          bold, italic, index, code);
     if (!res && priv->family_default) {
         res =
-            _select_font(library, priv, priv->family_default, 0, bold,
+            select_font(library, priv, priv->family_default, 0, bold,
                          italic, index, code);
         if (res)
             ass_msg(library, MSGL_WARN, "fontconfig_select: Using default "
@@ -263,7 +334,7 @@ char *fontconfig_select(ASS_Library *library, FCInstance *priv,
                 res, *index);
     }
     if (!res) {
-        res = _select_font(library, priv, "Arial", 0, bold, italic,
+        res = select_font(library, priv, "Arial", 0, bold, italic,
                            index, code);
         if (res)
             ass_msg(library, MSGL_WARN, "fontconfig_select: Using 'Arial' "
@@ -283,8 +354,8 @@ char *fontconfig_select(ASS_Library *library, FCInstance *priv,
  * \param library library object
  * \param ftlibrary freetype library object
  * \param idx index of the processed font in library->fontdata
- * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
- * With older FontConfig versions, save the font to ~/.mplayer/fonts.
+ *
+ * Builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace.
 */
 static void process_fontdata(FCInstance *priv, ASS_Library *library,
                              FT_Library ftlibrary, int idx)
@@ -311,7 +382,7 @@ static void process_fontdata(FCInstance *priv, ASS_Library *library,
         num_faces = face->num_faces;
 
         pattern =
-            FcFreeTypeQueryFace(face, (unsigned char *) name, 0,
+            FcFreeTypeQueryFace(face, (unsigned char *) name, face_index,
                                 FcConfigGetBlanks(priv->config));
         if (!pattern) {
             ass_msg(library, MSGL_WARN, "%s failed", "FcFreeTypeQueryFace");
@@ -388,7 +459,7 @@ FCInstance *fontconfig_init(ASS_Library *library,
         process_fontdata(priv, library, ftlibrary, i);
 
     if (dir) {
-        ass_msg(library, MSGL_INFO, "Updating font cache");
+        ass_msg(library, MSGL_V, "Updating font cache");
 
         rc = FcConfigAppFontAddDir(priv->config, (const FcChar8 *) dir);
         if (!rc) {
@@ -448,14 +519,13 @@ int fontconfig_update(FCInstance *priv)
 
 void fontconfig_done(FCInstance *priv)
 {
+
+    if (priv) {
 #ifdef CONFIG_FONTCONFIG
-    if (priv && priv->config)
         FcConfigDestroy(priv->config);
 #endif
-    if (priv && priv->path_default)
         free(priv->path_default);
-    if (priv && priv->family_default)
         free(priv->family_default);
-    if (priv)
-        free(priv);
+    }
+    free(priv);
 }
diff --git a/aegisub/libass/ass_fontconfig.h b/aegisub/libass/ass_fontconfig.h
index ad5b9f0e46fe220b0b7d943f9eceedffd53313be..396fb72d0d3c5e15dd82437d8a1c731b6bfc589f 100644
--- a/aegisub/libass/ass_fontconfig.h
+++ b/aegisub/libass/ass_fontconfig.h
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_FONTCONFIG_H
diff --git a/aegisub/libass/ass_library.c b/aegisub/libass/ass_library.c
index 53b91af1699a60151e28bc53194f9f4e49be04b5..5bca64485adfce6314685b3c601852d789d1ccd7 100644
--- a/aegisub/libass/ass_library.c
+++ b/aegisub/libass/ass_library.c
@@ -3,21 +3,21 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include "config.h"
+
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -57,8 +57,7 @@ void ass_library_done(ASS_Library *priv)
 
 void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir)
 {
-    if (priv->fonts_dir)
-        free(priv->fonts_dir);
+    free(priv->fonts_dir);
 
     priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0;
 }
@@ -77,8 +76,8 @@ void ass_set_style_overrides(ASS_Library *priv, char **list)
     if (priv->style_overrides) {
         for (p = priv->style_overrides; *p; ++p)
             free(*p);
-        free(priv->style_overrides);
     }
+    free(priv->style_overrides);
 
     if (!list)
         return;
diff --git a/aegisub/libass/ass_library.h b/aegisub/libass/ass_library.h
index e0db5c951f6562c526214cfb2348dd644d973037..8faf15e93f27a8966a1c68d20100cffdea55dfe4 100644
--- a/aegisub/libass/ass_library.h
+++ b/aegisub/libass/ass_library.h
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_LIBRARY_H
diff --git a/aegisub/libass/ass_parse.c b/aegisub/libass/ass_parse.c
index 0ccb5a2b1931502d08362e569380aefafbda3376..40aaf0430262f6b309eb4be81c408c68b051a95b 100644
--- a/aegisub/libass/ass_parse.c
+++ b/aegisub/libass/ass_parse.c
@@ -68,9 +68,15 @@ void update_font(ASS_Renderer *render_priv)
 {
     unsigned val;
     ASS_FontDesc desc;
-    desc.family = strdup(render_priv->state.family);
-    desc.treat_family_as_pattern =
-        render_priv->state.treat_family_as_pattern;
+    desc.treat_family_as_pattern = render_priv->state.treat_family_as_pattern;
+
+    if (render_priv->state.family[0] == '@') {
+        desc.vertical = 1;
+        desc.family = strdup(render_priv->state.family + 1);
+    } else {
+        desc.vertical = 0;
+        desc.family = strdup(render_priv->state.family);
+    }
 
     val = render_priv->state.bold;
     // 0 = normal, 1 = bold, >1 = exact weight
@@ -208,12 +214,14 @@ static char *parse_vector_clip(ASS_Renderer *render_priv, char *p)
 {
     int scale = 1;
     int res = 0;
-    ASS_Drawing *drawing;
+    ASS_Drawing *drawing = render_priv->state.clip_drawing;
 
+    if (drawing && drawing->glyph)
+        FT_Done_Glyph((FT_Glyph) drawing->glyph);
+    ass_drawing_free(drawing);
     render_priv->state.clip_drawing = ass_drawing_new(
         render_priv->fontconfig_priv,
         render_priv->state.font,
-        render_priv->settings.hinting,
         render_priv->ftlibrary);
     drawing = render_priv->state.clip_drawing;
     skipopt('(');
@@ -227,20 +235,6 @@ static char *parse_vector_clip(ASS_Renderer *render_priv, char *p)
     while (*p != ')' && *p != '}' && p != 0)
         ass_drawing_add_char(drawing, *p++);
     skipopt(')');
-    if (ass_drawing_parse(drawing, 1)) {
-        // We need to translate the clip according to screen borders
-        if (render_priv->settings.left_margin != 0 ||
-            render_priv->settings.top_margin != 0) {
-            FT_Vector trans = {
-                .x = int_to_d6(render_priv->settings.left_margin),
-                .y = -int_to_d6(render_priv->settings.top_margin),
-            };
-            FT_Outline_Translate(&drawing->glyph->outline, trans.x, trans.y);
-        }
-        ass_msg(render_priv->library, MSGL_DBG2,
-                "Parsed vector clip: scale %d, scales (%f, %f) string [%s]\n",
-                scale, drawing->scale_x, drawing->scale_y, drawing->text);
-    }
 
     return p;
 }
@@ -365,6 +359,22 @@ static char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
                 render_priv->state.hspacing * (1 - pwr) + val * pwr;
         else
             render_priv->state.hspacing = render_priv->state.style->Spacing;
+    } else if (mystrcmp(&p, "fs+")) {
+        double val;
+        if (mystrtod(&p, &val)) {
+            val = render_priv->state.font_size + pwr * val;
+        } else
+            val = render_priv->state.style->FontSize;
+        if (render_priv->state.font)
+            change_font_size(render_priv, val);
+    } else if (mystrcmp(&p, "fs-")) {
+        double val;
+        if (mystrtod(&p, &val))
+            val = render_priv->state.font_size - pwr * val;
+        else
+            val = render_priv->state.style->FontSize;
+        if (render_priv->state.font)
+            change_font_size(render_priv, val);
     } else if (mystrcmp(&p, "fs")) {
         double val;
         if (mystrtod(&p, &val))
@@ -461,8 +471,7 @@ static char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
             family[p - start] = '\0';
         } else
             family = strdup(render_priv->state.style->FontName);
-        if (render_priv->state.family)
-            free(render_priv->state.family);
+        free(render_priv->state.family);
         render_priv->state.family = family;
         update_font(render_priv);
     } else if (mystrcmp(&p, "alpha")) {
@@ -586,7 +595,7 @@ static char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
         for (cnt = 0; cnt < 3; ++cnt) {
             if (*p == '\\')
                 break;
-            v[cnt] = strtod(p, &p);
+            mystrtod(&p, &v[cnt]);
             skip(',');
         }
         if (cnt == 3) {
@@ -836,7 +845,7 @@ void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event)
     } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) {
         render_priv->state.scroll_direction = SCROLL_TB;
     } else {
-        ass_msg(render_priv->library, MSGL_V,
+        ass_msg(render_priv->library, MSGL_DBG2,
                 "Unknown transition effect: '%s'", event->Effect);
         return;
     }
@@ -894,7 +903,7 @@ unsigned get_next_char(ASS_Renderer *render_priv, char **str)
                     break;
             } else if (*p != '\\')
                 ass_msg(render_priv->library, MSGL_V,
-                        "Unable to parse: '%s'", p);
+                        "Unable to parse: '%.30s'", p);
             if (*p == 0)
                 break;
         }
@@ -918,6 +927,14 @@ unsigned get_next_char(ASS_Renderer *render_priv, char **str)
             p += 2;
             *str = p;
             return NBSP;
+        } else if (p[1] == '{') {
+            p += 2;
+            *str = p;
+            return '{';
+        } else if (p[1] == '}') {
+            p += 2;
+            *str = p;
+            return '}';
         }
     }
     chr = ass_utf8_get_char((char **) &p);
diff --git a/aegisub/libass/ass_render.c b/aegisub/libass/ass_render.c
index 6bc0c61baea07f0aeea83c881a0f0ae0ff2ff7cb..480fc4aeec95ac9eedb71ad01f310bd3ecd6649a 100644
--- a/aegisub/libass/ass_render.c
+++ b/aegisub/libass/ass_render.c
@@ -3,49 +3,31 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include "config.h"
 
 #include <assert.h>
 #include <math.h>
-#include <inttypes.h>
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include FT_STROKER_H
-#include FT_GLYPH_H
-#include FT_SYNTHESIS_H
-
-#include "ass.h"
-#include "ass_font.h"
-#include "ass_bitmap.h"
-#include "ass_cache.h"
-#include "ass_utils.h"
-#include "ass_fontconfig.h"
-#include "ass_library.h"
-#include "ass_drawing.h"
+
 #include "ass_render.h"
 #include "ass_parse.h"
 
 #define MAX_GLYPHS_INITIAL 1024
 #define MAX_LINES_INITIAL 64
 #define SUBPIXEL_MASK 63
-#define SUBPIXEL_ACCURACY 7    // d6 mask for subpixel accuracy adjustment
-#define GLYPH_CACHE_MAX 1000
-#define BITMAP_CACHE_MAX_SIZE 50 * 1048576
+#define SUBPIXEL_ACCURACY 7
 
 static void ass_lazy_track_init(ASS_Renderer *render_priv)
 {
@@ -119,27 +101,20 @@ ASS_Renderer *ass_renderer_init(ASS_Library *library)
 
     priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL;
     priv->text_info.max_lines = MAX_LINES_INITIAL;
-    priv->text_info.glyphs =
-        calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo));
+    priv->text_info.glyphs = calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo));
     priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo));
 
+    priv->settings.font_size_coeff = 1.;
+
   ass_init_exit:
     if (priv)
-        ass_msg(library, MSGL_INFO, "Init");
+        ass_msg(library, MSGL_V, "Init");
     else
         ass_msg(library, MSGL_ERR, "Init failed");
 
     return priv;
 }
 
-void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max,
-                          int bitmap_max)
-{
-    render_priv->cache.glyph_max = glyph_max ? glyph_max : GLYPH_CACHE_MAX;
-    render_priv->cache.bitmap_max_size = bitmap_max ? 1048576 * bitmap_max :
-                                         BITMAP_CACHE_MAX_SIZE;
-}
-
 static void free_list_clear(ASS_Renderer *render_priv)
 {
     if (render_priv->free_head) {
@@ -154,8 +129,6 @@ static void free_list_clear(ASS_Renderer *render_priv)
     }
 }
 
-static void ass_free_images(ASS_Image *img);
-
 void ass_renderer_done(ASS_Renderer *render_priv)
 {
     ass_font_cache_done(render_priv->cache.font_cache);
@@ -170,14 +143,13 @@ void ass_renderer_done(ASS_Renderer *render_priv)
         FT_Stroker_Done(render_priv->state.stroker);
         render_priv->state.stroker = 0;
     }
-    if (render_priv && render_priv->ftlibrary)
+    if (render_priv->ftlibrary)
         FT_Done_FreeType(render_priv->ftlibrary);
-    if (render_priv && render_priv->fontconfig_priv)
+    if (render_priv->fontconfig_priv)
         fontconfig_done(render_priv->fontconfig_priv);
-    if (render_priv && render_priv->synth_priv)
+    if (render_priv->synth_priv)
         ass_synth_done(render_priv->synth_priv);
-    if (render_priv && render_priv->eimg)
-        free(render_priv->eimg);
+    free(render_priv->eimg);
     free(render_priv->text_info.glyphs);
     free(render_priv->text_info.lines);
 
@@ -196,21 +168,85 @@ static ASS_Image *my_draw_bitmap(unsigned char *bitmap, int bitmap_w,
                                  int bitmap_h, int stride, int dst_x,
                                  int dst_y, uint32_t color)
 {
-    ASS_Image *img = calloc(1, sizeof(ASS_Image));
+    ASS_Image *img = malloc(sizeof(ASS_Image));
 
-    img->w = bitmap_w;
-    img->h = bitmap_h;
-    img->stride = stride;
-    img->bitmap = bitmap;
-    img->color = color;
-    img->dst_x = dst_x;
-    img->dst_y = dst_y;
+    if (img) {
+        img->w = bitmap_w;
+        img->h = bitmap_h;
+        img->stride = stride;
+        img->bitmap = bitmap;
+        img->color = color;
+        img->dst_x = dst_x;
+        img->dst_y = dst_y;
+    }
 
     return img;
 }
 
-static double x2scr_pos(ASS_Renderer *render_priv, double x);
-static double y2scr_pos(ASS_Renderer *render_priv, double y);
+/**
+ * \brief Mapping between script and screen coordinates
+ */
+static double x2scr(ASS_Renderer *render_priv, double x)
+{
+    return x * render_priv->orig_width_nocrop / render_priv->font_scale_x /
+        render_priv->track->PlayResX +
+        FFMAX(render_priv->settings.left_margin, 0);
+}
+static double x2scr_pos(ASS_Renderer *render_priv, double x)
+{
+    return x * render_priv->orig_width / render_priv->font_scale_x / render_priv->track->PlayResX +
+        render_priv->settings.left_margin;
+}
+static double x2scr_scaled(ASS_Renderer *render_priv, double x)
+{
+    return x * render_priv->orig_width_nocrop /
+        render_priv->track->PlayResX +
+        FFMAX(render_priv->settings.left_margin, 0);
+}
+static double x2scr_pos_scaled(ASS_Renderer *render_priv, double x)
+{
+    return x * render_priv->orig_width / render_priv->track->PlayResX +
+        render_priv->settings.left_margin;
+}
+/**
+ * \brief Mapping between script and screen coordinates
+ */
+static double y2scr(ASS_Renderer *render_priv, double y)
+{
+    return y * render_priv->orig_height_nocrop /
+        render_priv->track->PlayResY +
+        FFMAX(render_priv->settings.top_margin, 0);
+}
+static double y2scr_pos(ASS_Renderer *render_priv, double y)
+{
+    return y * render_priv->orig_height / render_priv->track->PlayResY +
+        render_priv->settings.top_margin;
+}
+
+// the same for toptitles
+static double y2scr_top(ASS_Renderer *render_priv, double y)
+{
+    if (render_priv->settings.use_margins)
+        return y * render_priv->orig_height_nocrop /
+            render_priv->track->PlayResY;
+    else
+        return y * render_priv->orig_height_nocrop /
+            render_priv->track->PlayResY +
+            FFMAX(render_priv->settings.top_margin, 0);
+}
+// the same for subtitles
+static double y2scr_sub(ASS_Renderer *render_priv, double y)
+{
+    if (render_priv->settings.use_margins)
+        return y * render_priv->orig_height_nocrop /
+            render_priv->track->PlayResY +
+            FFMAX(render_priv->settings.top_margin, 0)
+            + FFMAX(render_priv->settings.bottom_margin, 0);
+    else
+        return y * render_priv->orig_height_nocrop /
+            render_priv->track->PlayResY +
+            FFMAX(render_priv->settings.top_margin, 0);
+}
 
 /*
  * \brief Convert bitmap glyphs into ASS_Image list with inverse clipping
@@ -238,9 +274,9 @@ static ASS_Image **render_glyph_i(ASS_Renderer *render_priv,
     dst_y += bm->top;
 
     // we still need to clip against screen boundaries
-    zx = x2scr_pos(render_priv, 0);
+    zx = x2scr_pos_scaled(render_priv, 0);
     zy = y2scr_pos(render_priv, 0);
-    sx = x2scr_pos(render_priv, render_priv->track->PlayResX);
+    sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX);
     sy = y2scr_pos(render_priv, render_priv->track->PlayResY);
 
     x0 = 0;
@@ -295,6 +331,7 @@ static ASS_Image **render_glyph_i(ASS_Renderer *render_priv,
             img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + r[j].x0,
                 lbrk - r[j].x0, r[j].y1 - r[j].y0,
                 bm->w, dst_x + r[j].x0, dst_y + r[j].y0, color);
+            if (!img) break;
             *tail = img;
             tail = &img->next;
         }
@@ -303,6 +340,7 @@ static ASS_Image **render_glyph_i(ASS_Renderer *render_priv,
             img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + lbrk,
                 r[j].x1 - lbrk, r[j].y1 - r[j].y0,
                 bm->w, dst_x + lbrk, dst_y + r[j].y0, color2);
+            if (!img) break;
             *tail = img;
             tail = &img->next;
         }
@@ -384,6 +422,7 @@ render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y,
         img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0,
                              brk - b_x0, b_y1 - b_y0, bm->w,
                              dst_x + b_x0, dst_y + b_y0, color);
+        if (!img) return tail;
         *tail = img;
         tail = &img->next;
     }
@@ -393,6 +432,7 @@ render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y,
         img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk,
                              b_x1 - brk, b_y1 - b_y0, bm->w,
                              dst_x + brk, dst_y + b_y0, color2);
+        if (!img) return tail;
         *tail = img;
         tail = &img->next;
     }
@@ -464,7 +504,6 @@ render_overlap(ASS_Renderer *render_priv, ASS_Image **last_tail,
     cur_top = top - by;
 
     // Query cache
-    memset(&hk, 0, sizeof(hk));
     hk.a = (*last_tail)->bitmap;
     hk.b = (*tail)->bitmap;
     hk.aw = aw;
@@ -529,23 +568,73 @@ static void blend_vector_clip(ASS_Renderer *render_priv,
     FT_BitmapGlyph clip_bm;
     ASS_Image *cur;
     ASS_Drawing *drawing = render_priv->state.clip_drawing;
+    GlyphHashKey key;
+    GlyphHashValue *val;
     int error;
 
     if (!drawing)
         return;
 
-    // Rasterize it
-    FT_Glyph_Copy((FT_Glyph) drawing->glyph, &glyph);
-    error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
-    if (error) {
-        ass_msg(render_priv->library, MSGL_V,
-            "Clip vector rasterization failed: %d. Skipping.", error);
-        goto blend_vector_exit;
+    // Try to get mask from cache
+    ass_drawing_hash(drawing);
+    memset(&key, 0, sizeof(key));
+    key.ch = -2;
+    key.drawing_hash = drawing->hash;
+    val = cache_find_glyph(render_priv->cache.glyph_cache, &key);
+
+    if (val) {
+        clip_bm = (FT_BitmapGlyph) val->glyph;
+    } else {
+        GlyphHashValue v;
+
+        // Not found in cache, parse and rasterize it
+        glyph = (FT_Glyph) *ass_drawing_parse(drawing, 1);
+        if (!glyph) {
+            ass_msg(render_priv->library, MSGL_WARN,
+                    "Clip vector parsing failed. Skipping.");
+            goto blend_vector_error;
+        }
+
+        // We need to translate the clip according to screen borders
+        if (render_priv->settings.left_margin != 0 ||
+            render_priv->settings.top_margin != 0) {
+            FT_Vector trans = {
+                .x = int_to_d6(render_priv->settings.left_margin),
+                .y = -int_to_d6(render_priv->settings.top_margin),
+            };
+            FT_Outline_Translate(&drawing->glyph->outline,
+                                 trans.x, trans.y);
+        }
+
+        // Check glyph bounding box size
+        if (check_glyph_area(render_priv->library, glyph)) {
+            FT_Done_Glyph(glyph);
+            glyph = 0;
+            goto blend_vector_error;
+        }
+
+        ass_msg(render_priv->library, MSGL_DBG2,
+                "Parsed vector clip: scales (%f, %f) string [%s]\n",
+                drawing->scale_x, drawing->scale_y, drawing->text);
+
+        error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
+        if (error) {
+            ass_msg(render_priv->library, MSGL_WARN,
+                "Clip vector rasterization failed: %d. Skipping.", error);
+            FT_Done_Glyph(glyph);
+            glyph = 0;
+        }
+
+blend_vector_error:
+        clip_bm = (FT_BitmapGlyph) glyph;
+
+        // Add to cache
+        memset(&v, 0, sizeof(v));
+        v.glyph = glyph;
+        cache_add_glyph(render_priv->cache.glyph_cache, &key, &v);
     }
-    clip_bm = (FT_BitmapGlyph) glyph;
-    clip_bm->top = -clip_bm->top;
 
-    assert(clip_bm->bitmap.pitch >= 0);
+    if (!clip_bm) goto blend_vector_exit;
 
     // Iterate through bitmaps and blend/clip them
     for (cur = head; cur; cur = cur->next) {
@@ -563,7 +652,7 @@ static void blend_vector_clip(ASS_Renderer *render_priv,
         ah = cur->h;
         as = cur->stride;
         bx = clip_bm->left;
-        by = clip_bm->top;
+        by = -clip_bm->top;
         bw = clip_bm->bitmap.width;
         bh = clip_bm->bitmap.rows;
         bs = clip_bm->bitmap.pitch;
@@ -589,6 +678,7 @@ static void blend_vector_clip(ASS_Renderer *render_priv,
 
             // Allocate new buffer and add to free list
             nbuffer = malloc(as * ah);
+            if (!nbuffer) goto blend_vector_exit;
             free_list_add(render_priv, nbuffer);
 
             // Blend together
@@ -609,6 +699,7 @@ static void blend_vector_clip(ASS_Renderer *render_priv,
 
             // Allocate new buffer and add to free list
             nbuffer = calloc(as, ah);
+            if (!nbuffer) goto blend_vector_exit;
             free_list_add(render_priv, nbuffer);
 
             // Blend together
@@ -622,8 +713,6 @@ static void blend_vector_clip(ASS_Renderer *render_priv,
         cur->bitmap = nbuffer;
     }
 
-    // Free clip vector and its bitmap, we don't need it anymore
-    FT_Done_Glyph(glyph);
 blend_vector_exit:
     ass_drawing_free(render_priv->state.clip_drawing);
     render_priv->state.clip_drawing = 0;
@@ -633,8 +722,7 @@ blend_vector_exit:
  * \brief Convert TextInfo struct to ASS_Image list
  * Splits glyphs in halves when needed (for \kf karaoke).
  */
-static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x,
-                                int dst_y)
+static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x, int dst_y)
 {
     int pen_x, pen_y;
     int i;
@@ -731,62 +819,6 @@ static ASS_Image *render_text(ASS_Renderer *render_priv, int dst_x,
     return head;
 }
 
-/**
- * \brief Mapping between script and screen coordinates
- */
-static double x2scr(ASS_Renderer *render_priv, double x)
-{
-    return x * render_priv->orig_width_nocrop /
-        render_priv->track->PlayResX +
-        FFMAX(render_priv->settings.left_margin, 0);
-}
-static double x2scr_pos(ASS_Renderer *render_priv, double x)
-{
-    return x * render_priv->orig_width / render_priv->track->PlayResX +
-        render_priv->settings.left_margin;
-}
-
-/**
- * \brief Mapping between script and screen coordinates
- */
-static double y2scr(ASS_Renderer *render_priv, double y)
-{
-    return y * render_priv->orig_height_nocrop /
-        render_priv->track->PlayResY +
-        FFMAX(render_priv->settings.top_margin, 0);
-}
-static double y2scr_pos(ASS_Renderer *render_priv, double y)
-{
-    return y * render_priv->orig_height / render_priv->track->PlayResY +
-        render_priv->settings.top_margin;
-}
-
-// the same for toptitles
-static double y2scr_top(ASS_Renderer *render_priv, double y)
-{
-    if (render_priv->settings.use_margins)
-        return y * render_priv->orig_height_nocrop /
-            render_priv->track->PlayResY;
-    else
-        return y * render_priv->orig_height_nocrop /
-            render_priv->track->PlayResY +
-            FFMAX(render_priv->settings.top_margin, 0);
-}
-
-// the same for subtitles
-static double y2scr_sub(ASS_Renderer *render_priv, double y)
-{
-    if (render_priv->settings.use_margins)
-        return y * render_priv->orig_height_nocrop /
-            render_priv->track->PlayResY +
-            FFMAX(render_priv->settings.top_margin,
-                  0) + FFMAX(render_priv->settings.bottom_margin, 0);
-    else
-        return y * render_priv->orig_height_nocrop /
-            render_priv->track->PlayResY +
-            FFMAX(render_priv->settings.top_margin, 0);
-}
-
 static void compute_string_bbox(TextInfo *info, DBBox *bbox)
 {
     int i;
@@ -845,8 +877,6 @@ void reset_render_context(ASS_Renderer *render_priv)
     render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.;
     render_priv->state.fax = render_priv->state.fay = 0.;
     render_priv->state.wrap_style = render_priv->track->WrapStyle;
-
-    // FIXME: does not reset unsupported attributes.
 }
 
 /**
@@ -878,11 +908,10 @@ init_render_context(ASS_Renderer *render_priv, ASS_Event *event)
     render_priv->state.effect_type = EF_NONE;
     render_priv->state.effect_timing = 0;
     render_priv->state.effect_skip_timing = 0;
-    render_priv->state.drawing =
-        ass_drawing_new(render_priv->fontconfig_priv,
-                        render_priv->state.font,
-                        render_priv->settings.hinting,
-                        render_priv->ftlibrary);
+    ass_drawing_free(render_priv->state.drawing);
+    render_priv->state.drawing = ass_drawing_new(render_priv->fontconfig_priv,
+                                                 render_priv->state.font,
+                                                 render_priv->ftlibrary);
 
     apply_transition_effects(render_priv, event);
 }
@@ -896,88 +925,6 @@ static void free_render_context(ASS_Renderer *render_priv)
     render_priv->state.drawing = NULL;
 }
 
-// Calculate the cbox of a series of points
-static void
-get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end)
-{
-    box->xMin = box->yMin = INT_MAX;
-    box->xMax = box->yMax = INT_MIN;
-    int i;
-
-    for (i = start; i < end; i++) {
-        box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin;
-        box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax;
-        box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin;
-        box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax;
-    }
-}
-
-/**
- * \brief Fix-up stroker result for huge borders by removing the contours from
- * the outline that are harmful.
-*/
-static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x,
-                                 int border_y)
-{
-    int nc = glyph->outline.n_contours;
-    int begin, stop;
-    char modified = 0;
-    char *valid_cont;
-    int start = 0;
-    int end = -1;
-    FT_BBox *boxes = calloc(nc, sizeof(FT_BBox));
-    int i, j;
-
-    // Create a list of cboxes of the contours
-    for (i = 0; i < nc; i++) {
-        start = end + 1;
-        end = glyph->outline.contours[i];
-        get_contour_cbox(&boxes[i], glyph->outline.points, start, end);
-    }
-
-    // if a) contour's cbox is contained in another contours cbox
-    //    b) contour's height or width is smaller than the border*2
-    // the contour can be safely removed.
-    valid_cont = calloc(1, nc);
-    for (i = 0; i < nc; i++) {
-        valid_cont[i] = 1;
-        for (j = 0; j < nc; j++) {
-            if (i == j)
-                continue;
-            if (boxes[i].xMin >= boxes[j].xMin &&
-                boxes[i].xMax <= boxes[j].xMax &&
-                boxes[i].yMin >= boxes[j].yMin &&
-                boxes[i].yMax <= boxes[j].yMax) {
-                int width = boxes[i].xMax - boxes[i].xMin;
-                int height = boxes[i].yMax - boxes[i].yMin;
-                if (width < border_x * 2 || height < border_y * 2) {
-                    valid_cont[i] = 0;
-                    modified = 1;
-                    break;
-                }
-            }
-        }
-    }
-
-    // Zero-out contours that can be removed; much simpler than copying
-    if (modified) {
-        for (i = 0; i < nc; i++) {
-            if (valid_cont[i])
-                continue;
-            begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1;
-            stop = glyph->outline.contours[i];
-            for (j = begin; j <= stop; j++) {
-                glyph->outline.points[j].x = 0;
-                glyph->outline.points[j].y = 0;
-                glyph->outline.tags[j] = 0;
-            }
-        }
-    }
-
-    free(boxes);
-    free(valid_cont);
-}
-
 /*
  * Replace the outline of a glyph by a contour which makes up a simple
  * opaque rectangle.
@@ -989,8 +936,7 @@ static void draw_opaque_box(ASS_Renderer *render_priv, uint32_t ch,
     int i;
     int adv = d16_to_d6(glyph->advance.x);
     double scale_y = render_priv->state.scale_y;
-    double scale_x = render_priv->state.scale_x
-                     * render_priv->font_scale_x;
+    double scale_x = render_priv->state.scale_x;
     FT_OutlineGlyph og = (FT_OutlineGlyph) glyph;
     FT_Outline *ol;
 
@@ -1079,6 +1025,38 @@ static void stroke_outline_glyph(ASS_Renderer *render_priv,
     }
 }
 
+/**
+ * \brief Prepare glyph hash
+ */
+static void
+fill_glyph_hash(ASS_Renderer *priv, GlyphHashKey *key,
+                ASS_Drawing *drawing, uint32_t ch)
+{
+    if (drawing->hash) {
+        key->scale_x = double_to_d16(priv->state.scale_x);
+        key->scale_y = double_to_d16(priv->state.scale_y);
+        key->outline.x = priv->state.border_x * 0xFFFF;
+        key->outline.y = priv->state.border_y * 0xFFFF;
+        key->border_style = priv->state.style->BorderStyle;
+        key->drawing_hash = drawing->hash;
+        // not very clean, but works
+        key->size = drawing->scale;
+        key->ch = -1;
+    } else {
+        key->font = priv->state.font;
+        key->size = priv->state.font_size;
+        key->ch = ch;
+        key->bold = priv->state.bold;
+        key->italic = priv->state.italic;
+        key->scale_x = double_to_d16(priv->state.scale_x);
+        key->scale_y = double_to_d16(priv->state.scale_y);
+        key->outline.x = priv->state.border_x * 0xFFFF;
+        key->outline.y = priv->state.border_y * 0xFFFF;
+        key->flags = priv->state.flags;
+        key->border_style = priv->state.style->BorderStyle;
+    }
+}
+
 /**
  * \brief Get normal and outline (border) glyphs
  * \param symbol ucs4 char
@@ -1094,35 +1072,15 @@ get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info,
 {
     GlyphHashValue *val;
     GlyphHashKey key;
-    memset(&key, 0, sizeof(key));
 
-    if (drawing->hash) {
-        key.scale_x = double_to_d16(render_priv->state.scale_x);
-        key.scale_y = double_to_d16(render_priv->state.scale_y);
-        key.outline.x = render_priv->state.border_x * 0xFFFF;
-        key.outline.y = render_priv->state.border_y * 0xFFFF;
-        key.border_style = render_priv->state.style->BorderStyle;
-        key.drawing_hash = drawing->hash;
-    } else {
-        key.font = render_priv->state.font;
-        key.size = render_priv->state.font_size;
-        key.ch = symbol;
-        key.bold = render_priv->state.bold;
-        key.italic = render_priv->state.italic;
-        key.scale_x = double_to_d16(render_priv->state.scale_x);
-        key.scale_y = double_to_d16(render_priv->state.scale_y);
-        key.outline.x = render_priv->state.border_x * 0xFFFF;
-        key.outline.y = render_priv->state.border_y * 0xFFFF;
-        key.flags = render_priv->state.flags;
-        key.border_style = render_priv->state.style->BorderStyle;
-    }
+    memset(&key, 0, sizeof(key));
     memset(info, 0, sizeof(GlyphInfo));
 
+    fill_glyph_hash(render_priv, &key, drawing, symbol);
     val = cache_find_glyph(render_priv->cache.glyph_cache, &key);
     if (val) {
-        FT_Glyph_Copy(val->glyph, &info->glyph);
-        if (val->outline_glyph)
-            FT_Glyph_Copy(val->outline_glyph, &info->outline_glyph);
+        info->glyph = val->glyph;
+        info->outline_glyph = val->outline_glyph;
         info->bbox = val->bbox_scaled;
         info->advance.x = val->advance.x;
         info->advance.y = val->advance.y;
@@ -1135,7 +1093,7 @@ get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info,
         if (drawing->hash) {
             if(!ass_drawing_parse(drawing, 0))
                 return;
-            FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph);
+            info->glyph = (FT_Glyph) drawing->glyph;
         } else {
             info->glyph =
                 ass_font_get_glyph(render_priv->fontconfig_priv,
@@ -1145,6 +1103,7 @@ get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info,
         }
         if (!info->glyph)
             return;
+
         info->advance.x = d16_to_d6(info->glyph->advance.x);
         info->advance.y = d16_to_d6(info->glyph->advance.y);
         FT_Glyph_Get_CBox(info->glyph, FT_GLYPH_BBOX_SUBPIXELS, &info->bbox);
@@ -1158,8 +1117,9 @@ get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info,
                                          render_priv->border_scale),
                             double_to_d6(render_priv->state.border_y *
                                          render_priv->border_scale));
-        } else if (render_priv->state.border_x > 0 ||
-                   render_priv->state.border_y > 0) {
+        } else if ((render_priv->state.border_x > 0
+                    || render_priv->state.border_y > 0)
+                   && key.scale_x && key.scale_y) {
 
             FT_Glyph_Copy(info->glyph, &info->outline_glyph);
             stroke_outline_glyph(render_priv,
@@ -1171,9 +1131,8 @@ get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info,
         }
 
         memset(&v, 0, sizeof(v));
-        FT_Glyph_Copy(info->glyph, &v.glyph);
-        if (info->outline_glyph)
-            FT_Glyph_Copy(info->outline_glyph, &v.outline_glyph);
+        v.glyph = info->glyph;
+        v.outline_glyph = info->outline_glyph;
         v.advance = info->advance;
         v.bbox_scaled = info->bbox;
         if (drawing->hash) {
@@ -1184,10 +1143,81 @@ get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info,
     }
 }
 
-static void transform_3d(FT_Vector shift, FT_Glyph *glyph,
-                         FT_Glyph *glyph2, double frx, double fry,
-                         double frz, double fax, double fay, double scale,
-                         int yshift);
+/**
+ * \brief Apply transformation to outline points of a glyph
+ * Applies rotations given by frx, fry and frz and projects the points back
+ * onto the screen plane.
+ */
+static void
+transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry,
+                    double frz, double fax, double fay, double scale,
+                    int yshift)
+{
+    double sx = sin(frx);
+    double sy = sin(fry);
+    double sz = sin(frz);
+    double cx = cos(frx);
+    double cy = cos(fry);
+    double cz = cos(frz);
+    FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline;
+    FT_Vector *p = outline->points;
+    double x, y, z, xx, yy, zz;
+    int i, dist;
+
+    dist = 20000 * scale;
+    for (i = 0; i < outline->n_points; i++) {
+        x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y));
+        y = (double) p[i].y + shift.y + (-fay * p[i].x);
+        z = 0.;
+
+        xx = x * cz + y * sz;
+        yy = -(x * sz - y * cz);
+        zz = z;
+
+        x = xx;
+        y = yy * cx + zz * sx;
+        z = yy * sx - zz * cx;
+
+        xx = x * cy + z * sy;
+        yy = y;
+        zz = x * sy - z * cy;
+
+        zz = FFMAX(zz, 1000 - dist);
+
+        x = (xx * dist) / (zz + dist);
+        y = (yy * dist) / (zz + dist);
+        p[i].x = x - shift.x + 0.5;
+        p[i].y = y - shift.y + 0.5;
+    }
+}
+
+/**
+ * \brief Apply 3d transformation to several objects
+ * \param shift FreeType vector
+ * \param glyph FreeType glyph
+ * \param glyph2 FreeType glyph
+ * \param frx x-axis rotation angle
+ * \param fry y-axis rotation angle
+ * \param frz z-axis rotation angle
+ * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it.
+ */
+static void
+transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2,
+             double frx, double fry, double frz, double fax, double fay,
+             double scale, int yshift)
+{
+    frx = -frx;
+    frz = -frz;
+    if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) {
+        if (glyph && *glyph)
+            transform_3d_points(shift, *glyph, frx, fry, frz,
+                                fax, fay, scale, yshift);
+
+        if (glyph2 && *glyph2)
+            transform_3d_points(shift, *glyph2, frx, fry, frz,
+                                fax, fay, scale, yshift);
+    }
+}
 
 /**
  * \brief Get bitmaps for a glyph
@@ -1217,38 +1247,48 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
         info->bm = info->bm_o = info->bm_s = 0;
         if (info->glyph && info->symbol != '\n' && info->symbol != 0
             && !info->skip) {
+            FT_Glyph glyph;
+            FT_Glyph outline;
+            double scale_x = render_priv->font_scale_x;
+
+            FT_Glyph_Copy(info->glyph, &glyph);
+            FT_Glyph_Copy(info->outline_glyph, &outline);
             // calculating rotation shift vector (from rotation origin to the glyph basepoint)
-            shift.x = info->hash_key.shift_x;
-            shift.y = info->hash_key.shift_y;
-            fax_scaled = info->fax * render_priv->font_scale_x *
+            shift.x = key->shift_x;
+            shift.y = key->shift_y;
+            fax_scaled = info->fax *
                          render_priv->state.scale_x;
             fay_scaled = info->fay * render_priv->state.scale_y;
             // apply rotation
-            transform_3d(shift, &info->glyph, &info->outline_glyph,
+            transform_3d(shift, &glyph, &outline,
                          info->frx, info->fry, info->frz, fax_scaled,
                          fay_scaled, render_priv->font_scale, info->asc);
 
-            // subpixel shift
-            if (info->glyph)
-                FT_Outline_Translate(
-                    &((FT_OutlineGlyph) info->glyph)->outline,
-                    info->hash_key.advance.x,
-                    -info->hash_key.advance.y);
-            if (info->outline_glyph)
-                FT_Outline_Translate(
-                    &((FT_OutlineGlyph) info->outline_glyph)->outline,
-                    info->hash_key.advance.x,
-                    -info->hash_key.advance.y);
+            // PAR correction scaling
+            FT_Matrix m = { double_to_d16(scale_x), 0,
+                            0, double_to_d16(1.0) };
 
+            // subpixel shift
+            if (glyph) {
+                FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline;
+                if (scale_x != 1.0)
+                    FT_Outline_Transform(outl, &m);
+                FT_Outline_Translate(outl, key->advance.x, -key->advance.y);
+            }
+            if (outline) {
+                FT_Outline *outl = &((FT_OutlineGlyph) outline)->outline;
+                if (scale_x != 1.0)
+                    FT_Outline_Transform(outl, &m);
+                FT_Outline_Translate(outl, key->advance.x, -key->advance.y);
+            }
             // render glyph
             error = glyph_to_bitmap(render_priv->library,
                                     render_priv->synth_priv,
-                                    info->glyph, info->outline_glyph,
+                                    glyph, outline,
                                     &info->bm, &info->bm_o,
                                     &info->bm_s, info->be,
                                     info->blur * render_priv->border_scale,
-                                    info->hash_key.shadow_offset,
-                                    info->hash_key.border_style);
+                                    key->shadow_offset, key->border_style);
             if (error)
                 info->symbol = 0;
 
@@ -1256,15 +1296,12 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
             hash_val.bm_o = info->bm_o;
             hash_val.bm = info->bm;
             hash_val.bm_s = info->bm_s;
-            cache_add_bitmap(render_priv->cache.bitmap_cache,
-                             &(info->hash_key), &hash_val);
+            cache_add_bitmap(render_priv->cache.bitmap_cache, key, &hash_val);
+
+            FT_Done_Glyph(glyph);
+            FT_Done_Glyph(outline);
         }
     }
-    // deallocate glyphs
-    if (info->glyph)
-        FT_Done_Glyph(info->glyph);
-    if (info->outline_glyph)
-        FT_Done_Glyph(info->outline_glyph);
 }
 
 /**
@@ -1377,7 +1414,7 @@ static void trim_whitespace(ASS_Renderer *render_priv)
  * the difference in lengths between this two lines.
  * The result may not be optimal, but usually is good enough.
  *
- * FIXME: implement style 0 and 3 correctly, add support for style 1
+ * FIXME: implement style 0 and 3 correctly
  */
 static void
 wrap_lines_smart(ASS_Renderer *render_priv, double max_text_width)
@@ -1415,13 +1452,9 @@ wrap_lines_smart(ASS_Renderer *render_priv, double max_text_width)
             && (render_priv->state.wrap_style != 2)) {
             break_type = 1;
             break_at = last_space;
-            if (break_at == -1)
-                break_at = i - 1;
-            if (break_at == -1)
-                break_at = 0;
-            ass_msg(render_priv->library, MSGL_DBG2, "overfill at %d", i);
-            ass_msg(render_priv->library, MSGL_DBG2, "line break at %d",
-                    break_at);
+            if (break_at >= 0)
+                ass_msg(render_priv->library, MSGL_DBG2, "line break at %d",
+                        break_at);
         }
 
         if (break_at != -1) {
@@ -1646,82 +1679,43 @@ static void get_base_point(DBBox *bbox, int alignment, double *bx, double *by)
 }
 
 /**
- * \brief Apply transformation to outline points of a glyph
- * Applies rotations given by frx, fry and frz and projects the points back
- * onto the screen plane.
+ * Prepare bitmap hash key of a glyph
  */
 static void
-transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry,
-                    double frz, double fax, double fay, double scale,
-                    int yshift)
+fill_bitmap_hash(ASS_Renderer *priv, BitmapHashKey *hash_key,
+                 ASS_Drawing *drawing, FT_Vector pen, uint32_t code)
 {
-    double sx = sin(frx);
-    double sy = sin(fry);
-    double sz = sin(frz);
-    double cx = cos(frx);
-    double cy = cos(fry);
-    double cz = cos(frz);
-    FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline;
-    FT_Vector *p = outline->points;
-    double x, y, z, xx, yy, zz;
-    int i, dist;
-
-    dist = 20000 * scale;
-    for (i = 0; i < outline->n_points; i++) {
-        x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y));
-        y = (double) p[i].y + shift.y + (-fay * p[i].x);
-        z = 0.;
-
-        xx = x * cz + y * sz;
-        yy = -(x * sz - y * cz);
-        zz = z;
-
-        x = xx;
-        y = yy * cx + zz * sx;
-        z = yy * sx - zz * cx;
-
-        xx = x * cy + z * sy;
-        yy = y;
-        zz = x * sy - z * cy;
-
-        zz = FFMAX(zz, 1000 - dist);
-
-        x = (xx * dist) / (zz + dist);
-        y = (yy * dist) / (zz + dist);
-        p[i].x = x - shift.x + 0.5;
-        p[i].y = y - shift.y + 0.5;
-    }
-}
-
-/**
- * \brief Apply 3d transformation to several objects
- * \param shift FreeType vector
- * \param glyph FreeType glyph
- * \param glyph2 FreeType glyph
- * \param frx x-axis rotation angle
- * \param fry y-axis rotation angle
- * \param frz z-axis rotation angle
- * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it.
- */
-static void
-transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2,
-             double frx, double fry, double frz, double fax, double fay,
-             double scale, int yshift)
-{
-    frx = -frx;
-    frz = -frz;
-    if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) {
-        if (glyph && *glyph)
-            transform_3d_points(shift, *glyph, frx, fry, frz,
-                                fax, fay, scale, yshift);
-
-        if (glyph2 && *glyph2)
-            transform_3d_points(shift, *glyph2, frx, fry, frz,
-                                fax, fay, scale, yshift);
-    }
+    if (!drawing->hash) {
+        hash_key->font = priv->state.font;
+        hash_key->size = priv->state.font_size;
+        hash_key->bold = priv->state.bold;
+        hash_key->italic = priv->state.italic;
+    } else {
+        hash_key->drawing_hash = drawing->hash;
+        hash_key->size = drawing->scale;
+    }
+    hash_key->ch = code;
+    hash_key->outline.x = double_to_d16(priv->state.border_x);
+    hash_key->outline.y = double_to_d16(priv->state.border_y);
+    hash_key->scale_x = double_to_d16(priv->state.scale_x);
+    hash_key->scale_y = double_to_d16(priv->state.scale_y);
+    hash_key->frx = rot_key(priv->state.frx);
+    hash_key->fry = rot_key(priv->state.fry);
+    hash_key->frz = rot_key(priv->state.frz);
+    hash_key->fax = double_to_d16(priv->state.fax);
+    hash_key->fay = double_to_d16(priv->state.fay);
+    hash_key->be = priv->state.be;
+    hash_key->blur = priv->state.blur;
+    hash_key->border_style = priv->state.style->BorderStyle;
+    hash_key->shadow_offset.x = double_to_d6(
+            priv->state.shadow_x * priv->border_scale -
+            (int) (priv->state.shadow_x * priv->border_scale));
+    hash_key->shadow_offset.y = double_to_d6(
+            priv->state.shadow_y * priv->border_scale -
+            (int) (priv->state.shadow_y * priv->border_scale));
+    hash_key->flags = priv->state.flags;
 }
 
-
 /**
  * \brief Main ass rendering function, glues everything together
  * \param event event to render
@@ -1746,6 +1740,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
     double device_x = 0;
     double device_y = 0;
     TextInfo *text_info = &render_priv->text_info;
+    GlyphInfo *glyphs = render_priv->text_info.glyphs;
     ASS_Drawing *drawing;
 
     if (event->Style >= render_priv->track->n_styles) {
@@ -1779,7 +1774,6 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
         // Parse drawing
         if (drawing->i) {
             drawing->scale_x = render_priv->state.scale_x *
-                                     render_priv->font_scale_x *
                                      render_priv->font_scale;
             drawing->scale_y = render_priv->state.scale_y *
                                      render_priv->font_scale;
@@ -1800,7 +1794,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
         if (text_info->length >= text_info->max_glyphs) {
             // Raise maximum number of glyphs
             text_info->max_glyphs *= 2;
-            text_info->glyphs =
+            text_info->glyphs = glyphs =
                 realloc(text_info->glyphs,
                         sizeof(GlyphInfo) * text_info->max_glyphs);
         }
@@ -1811,139 +1805,82 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
             delta =
                 ass_font_get_kerning(render_priv->state.font, previous,
                                      code);
-            pen.x += delta.x * render_priv->state.scale_x
-                     * render_priv->font_scale_x;
-            pen.y += delta.y * render_priv->state.scale_y
-                     * render_priv->font_scale_x;
+            pen.x += delta.x * render_priv->state.scale_x;
+            pen.y += delta.y * render_priv->state.scale_y;
         }
 
         ass_font_set_transform(render_priv->state.font,
-                               render_priv->state.scale_x *
-                               render_priv->font_scale_x,
+                               render_priv->state.scale_x,
                                render_priv->state.scale_y, NULL);
 
         get_outline_glyph(render_priv, code,
-                          text_info->glyphs + text_info->length, drawing);
+                          glyphs + text_info->length, drawing);
 
         // Add additional space after italic to non-italic style changes
         if (text_info->length &&
-            text_info->glyphs[text_info->length - 1].hash_key.italic &&
+            glyphs[text_info->length - 1].hash_key.italic &&
             !render_priv->state.italic) {
             int back = text_info->length - 1;
-            GlyphInfo *og = &text_info->glyphs[back];
+            GlyphInfo *og = &glyphs[back];
             while (back && og->bbox.xMax - og->bbox.xMin == 0
                    && og->hash_key.italic)
-                og = &text_info->glyphs[--back];
+                og = &glyphs[--back];
             if (og->bbox.xMax > og->advance.x) {
                 // The FreeType oblique slants by 6/16
                 pen.x += og->bbox.yMax * 0.375;
             }
         }
 
-        text_info->glyphs[text_info->length].pos.x = pen.x;
-        text_info->glyphs[text_info->length].pos.y = pen.y;
+        glyphs[text_info->length].pos.x = pen.x;
+        glyphs[text_info->length].pos.y = pen.y;
 
-        pen.x += text_info->glyphs[text_info->length].advance.x;
+        pen.x += glyphs[text_info->length].advance.x;
         pen.x += double_to_d6(render_priv->state.hspacing *
                               render_priv->font_scale
                               * render_priv->state.scale_x);
-        pen.y += text_info->glyphs[text_info->length].advance.y;
+        pen.y += glyphs[text_info->length].advance.y;
         pen.y += (render_priv->state.fay * render_priv->state.scale_y) *
-                 text_info->glyphs[text_info->length].advance.x;
+                 glyphs[text_info->length].advance.x;
 
         previous = code;
 
-        text_info->glyphs[text_info->length].symbol = code;
-        text_info->glyphs[text_info->length].linebreak = 0;
+        glyphs[text_info->length].symbol = code;
+        glyphs[text_info->length].linebreak = 0;
         for (i = 0; i < 4; ++i) {
             uint32_t clr = render_priv->state.c[i];
             change_alpha(&clr,
                          mult_alpha(_a(clr), render_priv->state.fade), 1.);
-            text_info->glyphs[text_info->length].c[i] = clr;
+            glyphs[text_info->length].c[i] = clr;
         }
-        text_info->glyphs[text_info->length].effect_type =
-            render_priv->state.effect_type;
-        text_info->glyphs[text_info->length].effect_timing =
+        glyphs[text_info->length].effect_type = render_priv->state.effect_type;
+        glyphs[text_info->length].effect_timing =
             render_priv->state.effect_timing;
-        text_info->glyphs[text_info->length].effect_skip_timing =
+        glyphs[text_info->length].effect_skip_timing =
             render_priv->state.effect_skip_timing;
-        text_info->glyphs[text_info->length].be = render_priv->state.be;
-        text_info->glyphs[text_info->length].blur = render_priv->state.blur;
-        text_info->glyphs[text_info->length].shadow_x =
-            render_priv->state.shadow_x;
-        text_info->glyphs[text_info->length].shadow_y =
-            render_priv->state.shadow_y;
-        text_info->glyphs[text_info->length].frx = render_priv->state.frx;
-        text_info->glyphs[text_info->length].fry = render_priv->state.fry;
-        text_info->glyphs[text_info->length].frz = render_priv->state.frz;
-        text_info->glyphs[text_info->length].fax = render_priv->state.fax;
-        text_info->glyphs[text_info->length].fay = render_priv->state.fay;
+        glyphs[text_info->length].be = render_priv->state.be;
+        glyphs[text_info->length].blur = render_priv->state.blur;
+        glyphs[text_info->length].shadow_x = render_priv->state.shadow_x;
+        glyphs[text_info->length].shadow_y = render_priv->state.shadow_y;
+        glyphs[text_info->length].frx = render_priv->state.frx;
+        glyphs[text_info->length].fry = render_priv->state.fry;
+        glyphs[text_info->length].frz = render_priv->state.frz;
+        glyphs[text_info->length].fax = render_priv->state.fax;
+        glyphs[text_info->length].fay = render_priv->state.fay;
         if (drawing->hash) {
-            text_info->glyphs[text_info->length].asc = drawing->asc;
-            text_info->glyphs[text_info->length].desc = drawing->desc;
+            glyphs[text_info->length].asc = drawing->asc;
+            glyphs[text_info->length].desc = drawing->desc;
         } else {
             ass_font_get_asc_desc(render_priv->state.font, code,
-                                  &text_info->glyphs[text_info->length].asc,
-                                  &text_info->glyphs[text_info->length].desc);
+                                  &glyphs[text_info->length].asc,
+                                  &glyphs[text_info->length].desc);
 
-            text_info->glyphs[text_info->length].asc *=
-                render_priv->state.scale_y;
-            text_info->glyphs[text_info->length].desc *=
-                render_priv->state.scale_y;
+            glyphs[text_info->length].asc *= render_priv->state.scale_y;
+            glyphs[text_info->length].desc *= render_priv->state.scale_y;
         }
 
-        // fill bitmap_hash_key
-        if (!drawing->hash) {
-            text_info->glyphs[text_info->length].hash_key.font =
-                render_priv->state.font;
-            text_info->glyphs[text_info->length].hash_key.size =
-                render_priv->state.font_size;
-            text_info->glyphs[text_info->length].hash_key.bold =
-                render_priv->state.bold;
-            text_info->glyphs[text_info->length].hash_key.italic =
-                render_priv->state.italic;
-        } else
-            text_info->glyphs[text_info->length].hash_key.drawing_hash =
-                drawing->hash;
-        text_info->glyphs[text_info->length].hash_key.ch = code;
-        text_info->glyphs[text_info->length].hash_key.outline.x =
-            double_to_d16(render_priv->state.border_x);
-        text_info->glyphs[text_info->length].hash_key.outline.y =
-            double_to_d16(render_priv->state.border_y);
-        text_info->glyphs[text_info->length].hash_key.scale_x =
-            double_to_d16(render_priv->state.scale_x);
-        text_info->glyphs[text_info->length].hash_key.scale_y =
-            double_to_d16(render_priv->state.scale_y);
-        text_info->glyphs[text_info->length].hash_key.frx =
-            rot_key(render_priv->state.frx);
-        text_info->glyphs[text_info->length].hash_key.fry =
-            rot_key(render_priv->state.fry);
-        text_info->glyphs[text_info->length].hash_key.frz =
-            rot_key(render_priv->state.frz);
-        text_info->glyphs[text_info->length].hash_key.fax =
-            double_to_d16(render_priv->state.fax);
-        text_info->glyphs[text_info->length].hash_key.fay =
-            double_to_d16(render_priv->state.fay);
-        text_info->glyphs[text_info->length].hash_key.advance.x = pen.x;
-        text_info->glyphs[text_info->length].hash_key.advance.y = pen.y;
-        text_info->glyphs[text_info->length].hash_key.be =
-            render_priv->state.be;
-        text_info->glyphs[text_info->length].hash_key.blur =
-            render_priv->state.blur;
-        text_info->glyphs[text_info->length].hash_key.border_style =
-            render_priv->state.style->BorderStyle;
-        text_info->glyphs[text_info->length].hash_key.shadow_offset.x =
-            double_to_d6(
-                render_priv->state.shadow_x * render_priv->border_scale -
-                (int) (render_priv->state.shadow_x *
-                render_priv->border_scale));
-        text_info->glyphs[text_info->length].hash_key.shadow_offset.y =
-            double_to_d6(
-                render_priv->state.shadow_y * render_priv->border_scale -
-                (int) (render_priv->state.shadow_y *
-                render_priv->border_scale));
-        text_info->glyphs[text_info->length].hash_key.flags =
-            render_priv->state.flags;
+        // fill bitmap hash
+        fill_bitmap_hash(render_priv, &glyphs[text_info->length].hash_key,
+                         drawing, pen, code);
 
         text_info->length++;
 
@@ -1956,7 +1893,6 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
             drawing = render_priv->state.drawing =
                 ass_drawing_new(render_priv->fontconfig_priv,
                     render_priv->state.font,
-                    render_priv->settings.hinting,
                     render_priv->ftlibrary);
         }
     }
@@ -1967,6 +1903,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
         free_render_context(render_priv);
         return 1;
     }
+
     // depends on glyph x coordinates being monotonous, so it should be done before line wrap
     process_karaoke_effects(render_priv);
 
@@ -1976,14 +1913,11 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
     valign = alignment & 12;
 
     MarginL =
-        (event->MarginL) ? event->MarginL : render_priv->state.style->
-        MarginL;
+        (event->MarginL) ? event->MarginL : render_priv->state.style->MarginL;
     MarginR =
-        (event->MarginR) ? event->MarginR : render_priv->state.style->
-        MarginR;
+        (event->MarginR) ? event->MarginR : render_priv->state.style->MarginR;
     MarginV =
-        (event->MarginV) ? event->MarginV : render_priv->state.style->
-        MarginV;
+        (event->MarginV) ? event->MarginV : render_priv->state.style->MarginV;
 
     if (render_priv->state.evt_type != EVENT_HSCROLL) {
         double max_text_width;
@@ -2001,11 +1935,11 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
         last_break = -1;
         for (i = 1; i < text_info->length + 1; ++i) {   // (text_info->length + 1) is the end of the last line
             if ((i == text_info->length)
-                || text_info->glyphs[i].linebreak) {
+                || glyphs[i].linebreak) {
                 double width, shift = 0;
                 GlyphInfo *first_glyph =
-                    text_info->glyphs + last_break + 1;
-                GlyphInfo *last_glyph = text_info->glyphs + i - 1;
+                    glyphs + last_break + 1;
+                GlyphInfo *last_glyph = glyphs + i - 1;
 
                 while (first_glyph < last_glyph && first_glyph->skip)
                     first_glyph++;
@@ -2027,7 +1961,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
                     shift = (max_text_width - width) / 2.0;
                 }
                 for (j = last_break + 1; j < i; ++j) {
-                    text_info->glyphs[j].pos.x += double_to_d6(shift);
+                    glyphs[j].pos.x += double_to_d6(shift);
                 }
                 last_break = i - 1;
             }
@@ -2057,6 +1991,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
                       render_priv->state.scroll_shift) - (bbox.xMax -
                                                           bbox.xMin);
     }
+
     // y coordinate for everything except positioned events
     if (render_priv->state.evt_type == EVENT_NORMAL ||
         render_priv->state.evt_type == EVENT_HSCROLL) {
@@ -2072,7 +2007,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
             double scr_y;
             if (valign != VALIGN_SUB)
                 ass_msg(render_priv->library, MSGL_V,
-                       "Invalid valign, supposing 0 (subtitle)");
+                       "Invalid valign, assuming 0 (subtitle)");
             scr_y =
                 y2scr_sub(render_priv,
                           render_priv->track->PlayResY - MarginV);
@@ -2093,6 +2028,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
                       render_priv->state.clip_y1 -
                       render_priv->state.scroll_shift);
     }
+
     // positioned events are totally different
     if (render_priv->state.evt_type == EVENT_POSITIONED) {
         double base_x = 0;
@@ -2105,14 +2041,15 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
         device_y =
             y2scr_pos(render_priv, render_priv->state.pos_y) - base_y;
     }
+
     // fix clip coordinates (they depend on alignment)
     if (render_priv->state.evt_type == EVENT_NORMAL ||
         render_priv->state.evt_type == EVENT_HSCROLL ||
         render_priv->state.evt_type == EVENT_VSCROLL) {
         render_priv->state.clip_x0 =
-            x2scr(render_priv, render_priv->state.clip_x0);
+            x2scr_scaled(render_priv, render_priv->state.clip_x0);
         render_priv->state.clip_x1 =
-            x2scr(render_priv, render_priv->state.clip_x1);
+            x2scr_scaled(render_priv, render_priv->state.clip_x1);
         if (valign == VALIGN_TOP) {
             render_priv->state.clip_y0 =
                 y2scr_top(render_priv, render_priv->state.clip_y0);
@@ -2131,14 +2068,15 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
         }
     } else if (render_priv->state.evt_type == EVENT_POSITIONED) {
         render_priv->state.clip_x0 =
-            x2scr_pos(render_priv, render_priv->state.clip_x0);
+            x2scr_pos_scaled(render_priv, render_priv->state.clip_x0);
         render_priv->state.clip_x1 =
-            x2scr_pos(render_priv, render_priv->state.clip_x1);
+            x2scr_pos_scaled(render_priv, render_priv->state.clip_x1);
         render_priv->state.clip_y0 =
             y2scr_pos(render_priv, render_priv->state.clip_y0);
         render_priv->state.clip_y1 =
             y2scr_pos(render_priv, render_priv->state.clip_y1);
     }
+
     // calculate rotation parameters
     {
         DVector center;
@@ -2154,7 +2092,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
         }
 
         for (i = 0; i < text_info->length; ++i) {
-            GlyphInfo *info = text_info->glyphs + i;
+            GlyphInfo *info = glyphs + i;
 
             if (info->hash_key.frx || info->hash_key.fry
                 || info->hash_key.frz || info->hash_key.fax
@@ -2170,22 +2108,26 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
     }
 
     // convert glyphs to bitmaps
+    device_x *= render_priv->font_scale_x;
     for (i = 0; i < text_info->length; ++i) {
-        GlyphInfo *g = text_info->glyphs + i;
+        GlyphInfo *g = glyphs + i;
+        g->pos.x *= render_priv->font_scale_x;
         g->hash_key.advance.x =
             double_to_d6(device_x - (int) device_x +
             d6_to_double(g->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY;
         g->hash_key.advance.y =
             double_to_d6(device_y - (int) device_y +
             d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY;
-        get_bitmap_glyph(render_priv, text_info->glyphs + i);
+        get_bitmap_glyph(render_priv, glyphs + i);
     }
 
     memset(event_images, 0, sizeof(*event_images));
     event_images->top = device_y - text_info->lines[0].asc;
     event_images->height = text_info->height;
-    event_images->left = device_x + bbox.xMin + 0.5;
-    event_images->width = bbox.xMax - bbox.xMin + 0.5;
+    event_images->left =
+        (device_x + bbox.xMin * render_priv->font_scale_x) + 0.5;
+    event_images->width =
+        (bbox.xMax - bbox.xMin) * render_priv->font_scale_x + 0.5;
     event_images->detect_collisions = render_priv->state.detect_collisions;
     event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1;
     event_images->event = event;
@@ -2200,7 +2142,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
  * \brief deallocate image list
  * \param img list pointer
  */
-static void ass_free_images(ASS_Image *img)
+void ass_free_images(ASS_Image *img)
 {
     while (img) {
         ASS_Image *next = img->next;
@@ -2209,103 +2151,32 @@ static void ass_free_images(ASS_Image *img)
     }
 }
 
-static void ass_reconfigure(ASS_Renderer *priv)
-{
-    priv->render_id++;
-    priv->cache.glyph_cache =
-        ass_glyph_cache_reset(priv->cache.glyph_cache);
-    priv->cache.bitmap_cache =
-        ass_bitmap_cache_reset(priv->cache.bitmap_cache);
-    priv->cache.composite_cache =
-        ass_composite_cache_reset(priv->cache.composite_cache);
-    ass_free_images(priv->prev_images_root);
-    priv->prev_images_root = 0;
-}
-
-void ass_set_frame_size(ASS_Renderer *priv, int w, int h)
-{
-    if (priv->settings.frame_width != w || priv->settings.frame_height != h) {
-        priv->settings.frame_width = w;
-        priv->settings.frame_height = h;
-        if (priv->settings.aspect == 0.) {
-            priv->settings.aspect = ((double) w) / h;
-            priv->settings.storage_aspect = ((double) w) / h;
-        }
-        ass_reconfigure(priv);
-    }
-}
-
-void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r)
-{
-    if (priv->settings.left_margin != l ||
-        priv->settings.right_margin != r ||
-        priv->settings.top_margin != t
-        || priv->settings.bottom_margin != b) {
-        priv->settings.left_margin = l;
-        priv->settings.right_margin = r;
-        priv->settings.top_margin = t;
-        priv->settings.bottom_margin = b;
-        ass_reconfigure(priv);
-    }
-}
-
-void ass_set_use_margins(ASS_Renderer *priv, int use)
-{
-    priv->settings.use_margins = use;
-}
-
-void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar)
+/**
+ * \brief Check cache limits and reset cache if they are exceeded
+ */
+static void check_cache_limits(ASS_Renderer *priv, CacheStore *cache)
 {
-    if (priv->settings.aspect != dar || priv->settings.storage_aspect != sar) {
-        priv->settings.aspect = dar;
-        priv->settings.storage_aspect = sar;
-        ass_reconfigure(priv);
+    if (cache->bitmap_cache->cache_size > cache->bitmap_max_size) {
+        ass_msg(priv->library, MSGL_V,
+                "Hitting hard bitmap cache limit (was: %ld bytes), "
+                "resetting.", (long) cache->bitmap_cache->cache_size);
+        cache->bitmap_cache = ass_bitmap_cache_reset(cache->bitmap_cache);
+        cache->composite_cache = ass_composite_cache_reset(
+            cache->composite_cache);
+        ass_free_images(priv->prev_images_root);
+        priv->prev_images_root = 0;
     }
-}
 
-void ass_set_font_scale(ASS_Renderer *priv, double font_scale)
-{
-    if (priv->settings.font_size_coeff != font_scale) {
-        priv->settings.font_size_coeff = font_scale;
-        ass_reconfigure(priv);
-    }
-}
-
-void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht)
-{
-    if (priv->settings.hinting != ht) {
-        priv->settings.hinting = ht;
-        ass_reconfigure(priv);
+    if (cache->glyph_cache->count > cache->glyph_max
+        || cache->glyph_cache->cache_size > cache->bitmap_max_size) {
+        ass_msg(priv->library, MSGL_V,
+            "Hitting hard glyph cache limit (was: %d glyphs, %ld bytes), "
+            "resetting.",
+            cache->glyph_cache->count, (long) cache->glyph_cache->cache_size);
+        cache->glyph_cache = ass_glyph_cache_reset(cache->glyph_cache);
     }
 }
 
-void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing)
-{
-    priv->settings.line_spacing = line_spacing;
-}
-
-void ass_set_fonts(ASS_Renderer *priv, const char *default_font,
-                   const char *default_family, int fc, const char *config,
-                   int update)
-{
-    free(priv->settings.default_font);
-    free(priv->settings.default_family);
-    priv->settings.default_font = default_font ? strdup(default_font) : 0;
-    priv->settings.default_family =
-        default_family ? strdup(default_family) : 0;
-
-    if (priv->fontconfig_priv)
-        fontconfig_done(priv->fontconfig_priv);
-    priv->fontconfig_priv =
-        fontconfig_init(priv->library, priv->ftlibrary, default_family,
-                        default_font, fc, config, update);
-}
-
-int ass_fonts_update(ASS_Renderer *render_priv)
-{
-    return fontconfig_update(render_priv->fontconfig_priv);
-}
-
 /**
  * \brief Start a new frame
  */
@@ -2314,7 +2185,6 @@ ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track,
                 long long now)
 {
     ASS_Settings *settings_priv = &render_priv->settings;
-    CacheStore *cache = &render_priv->cache;
 
     if (!render_priv->settings.frame_width
         && !render_priv->settings.frame_height)
@@ -2323,27 +2193,14 @@ ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track,
     if (render_priv->library != track->library)
         return 1;
 
+    if (!render_priv->fontconfig_priv)
+        return 1;
+
     free_list_clear(render_priv);
 
     if (track->n_events == 0)
         return 1;               // nothing to do
 
-    render_priv->width = settings_priv->frame_width;
-    render_priv->height = settings_priv->frame_height;
-    render_priv->orig_width =
-        settings_priv->frame_width - settings_priv->left_margin -
-        settings_priv->right_margin;
-    render_priv->orig_height =
-        settings_priv->frame_height - settings_priv->top_margin -
-        settings_priv->bottom_margin;
-    render_priv->orig_width_nocrop =
-        settings_priv->frame_width - FFMAX(settings_priv->left_margin,
-                                           0) -
-        FFMAX(settings_priv->right_margin, 0);
-    render_priv->orig_height_nocrop =
-        settings_priv->frame_height - FFMAX(settings_priv->top_margin,
-                                            0) -
-        FFMAX(settings_priv->bottom_margin, 0);
     render_priv->track = track;
     render_priv->time = now;
 
@@ -2365,23 +2222,7 @@ ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track,
     render_priv->prev_images_root = render_priv->images_root;
     render_priv->images_root = 0;
 
-    if (cache->bitmap_cache->cache_size > cache->bitmap_max_size) {
-        ass_msg(render_priv->library, MSGL_V,
-                "Hitting hard bitmap cache limit (was: %ld bytes), "
-                "resetting.", (long) cache->bitmap_cache->cache_size);
-        cache->bitmap_cache = ass_bitmap_cache_reset(cache->bitmap_cache);
-        cache->composite_cache = ass_composite_cache_reset(
-            cache->composite_cache);
-        ass_free_images(render_priv->prev_images_root);
-        render_priv->prev_images_root = 0;
-    }
-
-    if (cache->glyph_cache->count > cache->glyph_max) {
-        ass_msg(render_priv->library, MSGL_V,
-            "Hitting hard glyph cache limit (was: %ld glyphs), resetting.",
-            (long) cache->glyph_cache->count);
-        cache->glyph_cache = ass_glyph_cache_reset(cache->glyph_cache);
-    }
+    check_cache_limits(render_priv, &render_priv->cache);
 
     return 0;
 }
@@ -2505,7 +2346,7 @@ fix_collisions(ASS_Renderer *render_priv, EventImages *imgs, int cnt)
             s.hb = priv->left + priv->width;
             if (priv->height != imgs[i].height) {       // no, it's not
                 ass_msg(render_priv->library, MSGL_WARN,
-                        "Warning! Event height has changed");
+                        "Event height has changed");
                 priv->top = 0;
                 priv->height = 0;
                 priv->left = 0;
diff --git a/aegisub/libass/ass_render.h b/aegisub/libass/ass_render.h
index 6d9db23fb1637ad69156d847066f4a367ff2c8df..89bffb0127ad41a7cbf9ee4d2a64aaae231bfa90 100644
--- a/aegisub/libass/ass_render.h
+++ b/aegisub/libass/ass_render.h
@@ -4,19 +4,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_RENDER_H
@@ -38,6 +36,9 @@
 #include "ass_library.h"
 #include "ass_drawing.h"
 
+#define GLYPH_CACHE_MAX 1000
+#define BITMAP_CACHE_MAX_SIZE 30 * 1048576
+
 typedef struct {
     double xMin;
     double xMax;
@@ -258,5 +259,6 @@ typedef struct {
 } Segment;
 
 void reset_render_context(ASS_Renderer *render_priv);
+void ass_free_images(ASS_Image *img);
 
 #endif /* LIBASS_RENDER_H */
diff --git a/aegisub/libass/ass_render_api.c b/aegisub/libass/ass_render_api.c
new file mode 100644
index 0000000000000000000000000000000000000000..65cfa58d07f57eb3957fc4768883c61f27008deb
--- /dev/null
+++ b/aegisub/libass/ass_render_api.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2006 Evgeniy Stepanov <eugeni.stepanov@gmail.com>
+ * Copyright (C) 2010 Grigori Goronzy <greg@geekmind.org>
+ *
+ * This file is part of libass.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+#include "ass_render.h"
+
+static void ass_reconfigure(ASS_Renderer *priv)
+{
+    ASS_Settings *settings = &priv->settings;
+
+    priv->render_id++;
+    priv->cache.glyph_cache =
+        ass_glyph_cache_reset(priv->cache.glyph_cache);
+    priv->cache.bitmap_cache =
+        ass_bitmap_cache_reset(priv->cache.bitmap_cache);
+    priv->cache.composite_cache =
+        ass_composite_cache_reset(priv->cache.composite_cache);
+    ass_free_images(priv->prev_images_root);
+    priv->prev_images_root = 0;
+
+    priv->width = settings->frame_width;
+    priv->height = settings->frame_height;
+    priv->orig_width = settings->frame_width - settings->left_margin -
+        settings->right_margin;
+    priv->orig_height = settings->frame_height - settings->top_margin -
+        settings->bottom_margin;
+    priv->orig_width_nocrop =
+        settings->frame_width - FFMAX(settings->left_margin, 0) -
+        FFMAX(settings->right_margin, 0);
+    priv->orig_height_nocrop =
+        settings->frame_height - FFMAX(settings->top_margin, 0) -
+        FFMAX(settings->bottom_margin, 0);
+}
+
+void ass_set_frame_size(ASS_Renderer *priv, int w, int h)
+{
+    if (priv->settings.frame_width != w || priv->settings.frame_height != h) {
+        priv->settings.frame_width = w;
+        priv->settings.frame_height = h;
+        if (priv->settings.aspect == 0.) {
+            priv->settings.aspect = ((double) w) / h;
+            priv->settings.storage_aspect = ((double) w) / h;
+        }
+        ass_reconfigure(priv);
+    }
+}
+
+void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r)
+{
+    if (priv->settings.left_margin != l || priv->settings.right_margin != r ||
+        priv->settings.top_margin != t || priv->settings.bottom_margin != b) {
+        priv->settings.left_margin = l;
+        priv->settings.right_margin = r;
+        priv->settings.top_margin = t;
+        priv->settings.bottom_margin = b;
+        ass_reconfigure(priv);
+    }
+}
+
+void ass_set_use_margins(ASS_Renderer *priv, int use)
+{
+    priv->settings.use_margins = use;
+}
+
+void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar)
+{
+    if (priv->settings.aspect != dar || priv->settings.storage_aspect != sar) {
+        priv->settings.aspect = dar;
+        priv->settings.storage_aspect = sar;
+        ass_reconfigure(priv);
+    }
+}
+
+void ass_set_font_scale(ASS_Renderer *priv, double font_scale)
+{
+    if (priv->settings.font_size_coeff != font_scale) {
+        priv->settings.font_size_coeff = font_scale;
+        ass_reconfigure(priv);
+    }
+}
+
+void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht)
+{
+    if (priv->settings.hinting != ht) {
+        priv->settings.hinting = ht;
+        ass_reconfigure(priv);
+    }
+}
+
+void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing)
+{
+    priv->settings.line_spacing = line_spacing;
+}
+
+void ass_set_fonts(ASS_Renderer *priv, const char *default_font,
+                   const char *default_family, int fc, const char *config,
+                   int update)
+{
+    free(priv->settings.default_font);
+    free(priv->settings.default_family);
+    priv->settings.default_font = default_font ? strdup(default_font) : 0;
+    priv->settings.default_family =
+        default_family ? strdup(default_family) : 0;
+
+    if (priv->fontconfig_priv)
+        fontconfig_done(priv->fontconfig_priv);
+    priv->fontconfig_priv =
+        fontconfig_init(priv->library, priv->ftlibrary, default_family,
+                        default_font, fc, config, update);
+}
+
+int ass_fonts_update(ASS_Renderer *render_priv)
+{
+    return fontconfig_update(render_priv->fontconfig_priv);
+}
+
+void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max,
+                          int bitmap_max)
+{
+    render_priv->cache.glyph_max = glyph_max ? glyph_max : GLYPH_CACHE_MAX;
+    render_priv->cache.bitmap_max_size = bitmap_max ? 1048576 * bitmap_max :
+                                         BITMAP_CACHE_MAX_SIZE;
+}
diff --git a/aegisub/libass/ass_strtod.c b/aegisub/libass/ass_strtod.c
index 7b73630bc38fb22ba513fa9a72c32a63ab2cc46a..f55b37ae123d652357e7a17c12a91ab6b19b6c82 100644
--- a/aegisub/libass/ass_strtod.c
+++ b/aegisub/libass/ass_strtod.c
@@ -16,12 +16,14 @@
 #include <ctype.h>
 #include <errno.h>
 
+const
 static int maxExponent = 511;   /* Largest possible base 10 exponent.  Any
                                  * exponent larger than this will already
                                  * produce underflow or overflow, so there's
                                  * no need to worry about additional digits.
                                  */
 
+const
 static double powersOf10[] = {  /* Table giving binary powers of 10.  Entry */
     10.,                        /* is 10^2^i.  Used to convert decimal */
     100.,                       /* exponents into floating-point numbers. */
@@ -224,7 +226,7 @@ ass_strtod(string, endPtr)
         errno = ERANGE;
     }
     dblExp = 1.0;
-    for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
+    for (d = (double *) powersOf10; exp != 0; exp >>= 1, d += 1) {
         if (exp & 01) {
             dblExp *= *d;
         }
diff --git a/aegisub/libass/ass_types.h b/aegisub/libass/ass_types.h
index 63bc36c4026ab53b4198b0ce0456f45a435d04b1..6a6f1ae8626f45fc42900214af7093266bfb6736 100644
--- a/aegisub/libass/ass_types.h
+++ b/aegisub/libass/ass_types.h
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_TYPES_H
diff --git a/aegisub/libass/ass_utils.c b/aegisub/libass/ass_utils.c
index 59fdbdfb94e4d195d330c3957fe23556d2908a90..4c9d4bcc62843443f956260e5b974f68c7f2feae 100644
--- a/aegisub/libass/ass_utils.c
+++ b/aegisub/libass/ass_utils.c
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #include "config.h"
@@ -25,6 +23,7 @@
 #include <inttypes.h>
 #include <ft2build.h>
 #include FT_GLYPH_H
+#include <strings.h>
 
 #include "ass_library.h"
 #include "ass.h"
diff --git a/aegisub/libass/ass_utils.h b/aegisub/libass/ass_utils.h
index ad8574c1d69e838053b4595245cd742cfd606e51..327bb79cf415fb0b9dd3d57bbe7b7cff3e129390 100644
--- a/aegisub/libass/ass_utils.h
+++ b/aegisub/libass/ass_utils.h
@@ -3,19 +3,17 @@
  *
  * This file is part of libass.
  *
- * libass is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * libass is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with libass; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef LIBASS_UTILS_H